My Graphik
TXLib.h
Go to the documentation of this file.
1 //=================================================================================================================
2 // [These sections are for folding control in Code::Blocks] [$Date: 2020-05-15 02:04:56 +0400 $]
3 // [Best viewed with "Fold all on file open" option enabled] [Best screen/page width = 120 chars]
4 //
5 // [If RUSSIAN CHARS below are UNREADABLE, check this file codepage. It should be CP1251, NOT UTF-8 etc.]
6 //{ [Use RELOAD options in your IDE or editor (CLion / Visual Studio Code / ...), and do NOT use Convert.]
7 //=================================================================================================================
88 // $Copyright: (C) Ded (Ilya Dedinsky, http://txlib.ru) <mail@txlib.ru> $
89 //-----------------------------------------------------------------------------------------------------------------
96 //}
97 //=================================================================================================================
98 
99 #if !defined (__TXLIB_H_INCLUDED) // <<<<<<<<< THE CODE IS HERE, UNFOLD IT <<<<<<<<<<<<<<<<<<<<<<<<<
100 #define __TXLIB_H_INCLUDED
101 
102 //-----------------------------------------------------------------------------------------------------------------
103 //{ Version information and configuration
104 //-----------------------------------------------------------------------------------------------------------------
105 
106 //{----------------------------------------------------------------------------------------------------------------
128 //}----------------------------------------------------------------------------------------------------------------
130 
131 #define _TX_VER _TX_v_FROM_CVS ($VersionInfo: , TXLib.h, 00173a, 166, 2020-05-15 02:04:56 +0400, "Ded (Ilya Dedinsky, http://txlib.ru) <mail@txlib.ru>", $)
132 #define _TX_VERSION _TX_V_FROM_CVS ($VersionInfo: , TXLib.h, 00173a, 166, 2020-05-15 02:04:56 +0400, "Ded (Ilya Dedinsky, http://txlib.ru) <mail@txlib.ru>", $)
133 #define _TX_AUTHOR _TX_A_FROM_CVS ($VersionInfo: , TXLib.h, 00173a, 166, 2020-05-15 02:04:56 +0400, "Ded (Ilya Dedinsky, http://txlib.ru) <mail@txlib.ru>", $)
134 
136 #define _TX_v_FROM_CVS(_1,file,ver,rev,date,auth,_2) ((0x##ver##u << 16) | 0x##rev##u)
137 #define _TX_V_FROM_CVS(_1,file,ver,rev,date,auth,_2) "TXLib [Ver: " #ver ", Rev: " #rev ", Date: " #date "]"
138 #define _TX_A_FROM_CVS(_1,file,ver,rev,date,auth,_2) "Copyright (C) " auth
139 
142 //{----------------------------------------------------------------------------------------------------------------
149 //}----------------------------------------------------------------------------------------------------------------
150 
151 #if !defined (_TX_MODULE)
152  #define _TX_MODULE "TXLib"
153 #endif
154 
155 //{----------------------------------------------------------------------------------------------------------------
159 //}----------------------------------------------------------------------------------------------------------------
160 
161 #if defined (__GNUC__)
162 
163  #define _GCC_VER ( __GNUC__*100 + __GNUC_MINOR__*10 + __GNUC_PATCHLEVEL__ )
164 
165  #define __TX_COMPILER__ "GNU g++ " TX_QUOTE (__GNUC__) "." \
166  TX_QUOTE (__GNUC_MINOR__) "." \
167  TX_QUOTE (__GNUC_PATCHLEVEL__) \
168  ", std=" TX_QUOTE (__cplusplus)
169 
170 #elif defined (__clang__) || defined (__clang_major__)
171 
172  #define _CLANG_VER ( __clang_major__*100 + __clang_minor__*10 + __clang_patchlevel__ )
173 
174  #define __TX_COMPILER__ "Clang " TX_QUOTE (__clang_major__) "." \
175  TX_QUOTE (__clang_minor__) "." \
176  TX_QUOTE (__clang_patchlevel__) \
177  ", std=" TX_QUOTE (__cplusplus)
178 #elif defined (_MSC_VER)
179 
180  #define __TX_COMPILER__ "MSVS " TX_QUOTE (_MSC_VER) \
181  ", std=" TX_QUOTE (__cplusplus)
182 
183 #elif defined (__INTEL_COMPILER)
184 
185  #define __TX_COMPILER__ "Intel C++ " TX_QUOTE (__INTEL_COMPILER) \
186  ", std=" TX_QUOTE (__cplusplus)
187 #else
188 
189  #define __TX_COMPILER__ "Unknown C++, std=" TX_QUOTE (__cplusplus)
190  #endif
191 
193 
194 #define TX_QUOTE(sym) _TX_QUOTE (sym)
195 #define _TX_QUOTE(sym) #sym
196 
197 #define TX_JOIN(sym1, sym2) _TX_JOIN (sym1, sym2)
198 #define _TX_JOIN(sym1, sym2) sym1 ## sym2
199 
201 
202 #if (__cplusplus >= 201103L) || defined (_MSC_VER) && (_MSC_VER >= 1800) // MSVC 2013
203 
204  #define _TX_CPP11 1
205  #endif
206 
207 #if (__cplusplus >= 201103L) || defined (_MSC_VER) && (_MSC_VER >= 1900) // MSVC 2015
208 
209  #define _TX_CPP11_MSVC15 1
210  #endif
211 
212 //{----------------------------------------------------------------------------------------------------------------
216 //}----------------------------------------------------------------------------------------------------------------
217 
218 #if !defined (NDEBUG) && defined (_DEBUG)
219  #define _TX_BUILDMODE "DEBUG"
220 
221 #elif !defined (NDEBUG) && !defined (_DEBUG)
222  #define _TX_BUILDMODE "Debug"
223 
224 #elif defined (NDEBUG)
225  #define _TX_BUILDMODE "Release"
226 #endif
227 
228 //{----------------------------------------------------------------------------------------------------------------
232 //}----------------------------------------------------------------------------------------------------------------
233 
234 #define __TX_FILELINE__ __FILE__ " (" TX_QUOTE (__LINE__) ")"
235 
236 //{----------------------------------------------------------------------------------------------------------------
244 //}----------------------------------------------------------------------------------------------------------------
245 
246 #if defined (__GNUC__) || defined (__clang__) || defined (__clang_major__)
247  #define __TX_FUNCTION__ __PRETTY_FUNCTION__
248 
249 #elif defined (__FUNCSIG__)
250  #define __TX_FUNCTION__ __FUNCSIG__
251 
252 #elif defined (__FUNCTION__)
253  #define __TX_FUNCTION__ __FUNCTION__
254 
255 #elif defined (__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)
256  #define __TX_FUNCTION__ __FUNCTION__
257 
258 #elif defined (__BORLANDC__) && (__BORLANDC__ >= 0x550)
259  #define __TX_FUNCTION__ __FUNC__
260 
261 #elif defined (__cplusplus) && (__cplusplus >= 199711L)
262  #define __TX_FUNCTION__ __func__
263 
264 #elif defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
265  #define __TX_FUNCTION__ __func__
266 
267 #elif defined (__PYTHON__)
268  #error No Python. No. Using parseltongue languages can lead you to Slytherin.
269 
270 #else
271  #define __TX_FUNCTION__ "(" __TX_FILELINE__ ")"
272 
273 #endif
274 
275 #if !defined (__func__) && defined (__FUNCTION__)
276  #define __func__ __FUNCTION__
277 
278 #endif
279 
280 //}
281 //-----------------------------------------------------------------------------------------------------------------
282 
283 //-----------------------------------------------------------------------------------------------------------------
284 //{ Compiler- and platform-specific
286 //-----------------------------------------------------------------------------------------------------------------
288 
289 #if !defined (__cplusplus)
290 
291  #ifdef __GNUC__
292  #error
293  #error ---------------------------------------------------------------------------------------
294  #endif
295  #error TXLib.h: Must use C++ to compile TXLib.h. Now you are using C only.
296  #error
297  #error CHECK source file EXTENSION. Maybe it is ".C". It must be ".CPP".
298  #error If your file is named, for example, "Untitled.C", go to menu [File],
299  #error then [Save As] and rename it to "Untitled.CPP". Please do NOT use spaces.
300  #error ---------------------------------------------------------------------------------------
301  #error
302 
303 #endif
304 
305 //-----------------------------------------------------------------------------------------------------------------
306 
307 #if !defined (WIN32) && !defined (__WIN32__) && !defined(_WIN32) && !defined(_WIN32_WINNT) && !defined (__CYGWIN__)
308 
309  #ifdef __GNUC__
310  #error
311  #error ---------------------------------------------------------------------------------------
312  #endif
313  #error TXLib.h: Windows (MSVC/Win32 or GCC/MinGW or Cygwin) is the only supported OS, sorry.
314  #error
315  #error In Linux or MacOS, you should write your own TXLib and share it with your friends, or use wine.
316  #error ---------------------------------------------------------------------------------------
317  #error
318 
319 #endif
320 
321 //-----------------------------------------------------------------------------------------------------------------
322 
323 #if defined (UNICODE) || defined (_UNICODE)
324 
325  #ifdef __GNUC__
326  #warning TXLib.h: Disabling the UNICODE
327  #endif
328 
329  #undef UNICODE // Burn Unicode, burn
330  #undef _UNICODE
331 
332  #if defined (_WINDOWS_H) || defined (_INC_WINDOWS) || defined (_WINDOWS_) || defined (__WINDOWS__)
333 
334  #ifdef __GNUC__
335  #error
336  #error ---------------------------------------------------------------------------------------
337  #endif
338  #error TXLib.h: Should include "TXLib.h" BEFORE or INSTEAD of <Windows.h> in UNICODE mode.
339  #error
340  #error REARRANGE your #include directives, or DISABLE the UNICODE mode by #undef UNICODE/_UNICODE.
341  #error ---------------------------------------------------------------------------------------
342  #error
343 
344  #endif
345 
346 #endif
347 
348 //-----------------------------------------------------------------------------------------------------------------
349 
350 #if defined (__STRICT_ANSI__) // Try to extend strict ANSI rules
351 
352  #undef __STRICT_ANSI__
353  #define __STRICT_ANSI__UNDEFINED
354 
355  #if defined (_STRING_H_) || defined (_INC_STRING) || defined (_STDIO_H_) || defined (_INC_STDIO)
356 
357  #ifdef __GNUC__
358  #error
359  #error ---------------------------------------------------------------------------------------
360  #endif
361  #error TXLib.h: Should include "TXLib.h" BEFORE <string.h> or <stdio.h> in Strict ANSI mode.
362  #error
363  #error REARRANGE your #include directives, or DISABLE ANSI-compliancy by #undef __STRICT_ANSI__.
364  #error ---------------------------------------------------------------------------------------
365  #error
366 
367  #endif
368 
369 #endif
370 
371 //-----------------------------------------------------------------------------------------------------------------
372 
373 #if defined (__GNUC__)
374 
375  #pragma GCC diagnostic ignored "-Wpragmas"
376 
377  #pragma GCC diagnostic warning "-Wall"
378  #pragma GCC diagnostic warning "-Weffc++"
379  #pragma GCC diagnostic warning "-Wextra"
380 
381  #pragma GCC diagnostic warning "-Waggressive-loop-optimizations"
382  #pragma GCC diagnostic warning "-Walloc-zero"
383  #pragma GCC diagnostic warning "-Walloca"
384  #pragma GCC diagnostic warning "-Walloca-larger-than=8192"
385  #pragma GCC diagnostic warning "-Warray-bounds"
386  #pragma GCC diagnostic warning "-Wcast-align"
387  #pragma GCC diagnostic warning "-Wcast-qual"
388  #pragma GCC diagnostic warning "-Wchar-subscripts"
389  #pragma GCC diagnostic warning "-Wconditionally-supported"
390  #pragma GCC diagnostic warning "-Wconversion"
391  #pragma GCC diagnostic warning "-Wctor-dtor-privacy"
392  #pragma GCC diagnostic warning "-Wdangling-else"
393  #pragma GCC diagnostic warning "-Wduplicated-branches"
394  #pragma GCC diagnostic warning "-Wempty-body"
395  #pragma GCC diagnostic warning "-Wfloat-equal"
396  #pragma GCC diagnostic warning "-Wformat-nonliteral"
397  #pragma GCC diagnostic warning "-Wformat-overflow=2"
398  #pragma GCC diagnostic warning "-Wformat-security"
399  #pragma GCC diagnostic warning "-Wformat-signedness"
400  #pragma GCC diagnostic warning "-Wformat-truncation=2"
401  #pragma GCC diagnostic warning "-Wformat=2"
402  #pragma GCC diagnostic warning "-Wlarger-than=8192"
403  #pragma GCC diagnostic warning "-Wlogical-op"
404  #pragma GCC diagnostic warning "-Wmissing-declarations"
405  #pragma GCC diagnostic warning "-Wnarrowing"
406  #pragma GCC diagnostic warning "-Wnon-virtual-dtor"
407  #pragma GCC diagnostic warning "-Wnonnull"
408  #pragma GCC diagnostic warning "-Wopenmp-simd"
409  #pragma GCC diagnostic warning "-Woverloaded-virtual"
410  #pragma GCC diagnostic warning "-Wpacked"
411  #pragma GCC diagnostic warning "-Wpointer-arith"
412  #pragma GCC diagnostic warning "-Wredundant-decls"
413  #pragma GCC diagnostic warning "-Wrestrict"
414  #pragma GCC diagnostic warning "-Wshadow"
415  #pragma GCC diagnostic warning "-Wsign-promo"
416  #pragma GCC diagnostic warning "-Wstack-usage=8192"
417  #pragma GCC diagnostic warning "-Wstrict-aliasing"
418  #pragma GCC diagnostic warning "-Wstrict-null-sentinel"
419  #pragma GCC diagnostic warning "-Wstrict-overflow=2"
420  #pragma GCC diagnostic warning "-Wstringop-overflow=4"
421  #pragma GCC diagnostic warning "-Wsuggest-attribute=noreturn"
422  #pragma GCC diagnostic warning "-Wsuggest-final-methods"
423  #pragma GCC diagnostic warning "-Wsuggest-final-types"
424  #pragma GCC diagnostic warning "-Wsuggest-override"
425  #pragma GCC diagnostic warning "-Wswitch-default"
426  #pragma GCC diagnostic warning "-Wswitch-enum"
427  #pragma GCC diagnostic warning "-Wsync-nand"
428  #pragma GCC diagnostic warning "-Wundef"
429  #pragma GCC diagnostic warning "-Wunused"
430  #pragma GCC diagnostic warning "-Wvarargs"
431  #pragma GCC diagnostic warning "-Wvariadic-macros"
432  #pragma GCC diagnostic warning "-Wvla-larger-than=8192"
433 
434  #pragma GCC diagnostic error "-Wsizeof-array-argument"
435 
436  #pragma GCC diagnostic ignored "-Winline"
437  #pragma GCC diagnostic ignored "-Wliteral-suffix"
438  #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
439  #pragma GCC diagnostic ignored "-Wnonnull-compare"
440  #pragma GCC diagnostic ignored "-Wold-style-cast"
441  #pragma GCC diagnostic ignored "-Wunreachable-code"
442  #pragma GCC diagnostic ignored "-Wunused-const-variable"
443  #pragma GCC diagnostic ignored "-Wunused-function"
444 
445  #pragma GCC diagnostic warning "-Wpragmas"
446 
447  //{ These warning settings for TXLib.h only and will be re-enabled at end of file:
448 
449  #pragma GCC push_options
450  #pragma GCC diagnostic push
451 
452  #pragma GCC diagnostic ignored "-Wpragmas"
453 
454  #pragma GCC diagnostic ignored "-Waddress"
455  #pragma GCC diagnostic ignored "-Wclobbered"
456  #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
457  #pragma GCC diagnostic ignored "-Wfloat-equal"
458  #pragma GCC diagnostic ignored "-Wformat-nonliteral"
459  #pragma GCC diagnostic ignored "-Wlarger-than="
460  #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
461  #pragma GCC diagnostic ignored "-Wredundant-decls"
462  #pragma GCC diagnostic ignored "-Wshadow"
463  #pragma GCC diagnostic ignored "-Wsign-conversion"
464  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
465  #pragma GCC diagnostic ignored "-Wunused-label" // Just for fun in _txCanvas_OnCmdAbout()
466  #pragma GCC diagnostic ignored "-Wunused-value"
467  #pragma GCC diagnostic ignored "-Wformat-zero-length"
468  #pragma GCC diagnostic ignored "-Wpacked-not-aligned"
469  #pragma GCC optimize "no-strict-aliasing"
470 
471  #if (__cplusplus < 201402L)
472  #pragma GCC diagnostic ignored "-Wsuggest-override"
473  #endif
474 
475  #pragma GCC diagnostic warning "-Wpragmas"
476 
477  #if defined (__CYGWIN__) && !defined (_TX_TESTING)
478  #pragma GCC system_header // This is not a fair play, but this is the only way to deal with Cygwin :(
479  #endif
480 
481  //}
482 
483  #define _tx_thread __thread
484  #define _tx_decltype(value) __decltype (value)
485 
486  #ifndef MINGW_HAS_SECURE_API
487  #define MINGW_HAS_SECURE_API 1
488  #endif
489 
490  #if defined (TX_USE_SFML)
491  #define _GLIBCXX_NDEBUG
492  #endif
493 
494  #ifndef _GLIBCXX_NDEBUG // TXLib enables _GLIBCXX_DEBUG by default. When using third-party libraries
495  #define _GLIBCXX_DEBUG // compiled without _GLIBCXX_DEBUG (SFML, for example), #define _GLIBCXX_NDEBUG
496  #define _GLIBCXX_DEBUG_PEDANTIC // *before* including TXLib.h.
497  #endif
498 
499  #if defined (_WIN64) // removed in x86 because printf ("%lg", double) failure, this prints 0 always
500  #ifndef __USE_MINGW_ANSI_STDIO
501  #define __USE_MINGW_ANSI_STDIO 1
502  #endif
503  #endif
504 
505  template <typename T>
506  inline T _txNOP (T value) { return value; } // To suppress performance warnings in assert etc.
507 
508  // From MinGW\include\float.h which is replaced by MinGW\lib\gcc\i686-pc-mingw32\x.x.x\include\float.h
509  extern "C" __declspec (dllimport) unsigned __cdecl _controlfp (unsigned control, unsigned mask);
510  extern "C" void __cdecl _fpreset ();
511 
512 #else
513 
514  #define __attribute__( attr )
515  #define _txNOP( value ) ( value )
516 
517 #endif
518 
519 //-----------------------------------------------------------------------------------------------------------------
520 
521 #if defined (__clang__) || defined (__clang_major__)
522 
523  #pragma clang diagnostic ignored "-Wunknown-pragmas"
524 
525  #pragma clang diagnostic warning "-Wall"
526  #pragma clang diagnostic warning "-Weffc++"
527  #pragma clang diagnostic warning "-Wextra"
528 
529  #pragma clang diagnostic warning "-Wcast-qual"
530  #pragma clang diagnostic warning "-Wchar-subscripts"
531  #pragma clang diagnostic warning "-Wconversion"
532  #pragma clang diagnostic warning "-Wctor-dtor-privacy"
533  #pragma clang diagnostic warning "-Wempty-body"
534  #pragma clang diagnostic warning "-Wfloat-equal"
535  #pragma clang diagnostic warning "-Wformat"
536  #pragma clang diagnostic warning "-Wformat-nonliteral"
537  #pragma clang diagnostic warning "-Wformat-security"
538  #pragma clang diagnostic warning "-Wmissing-declarations"
539  #pragma clang diagnostic warning "-Wnon-virtual-dtor"
540  #pragma clang diagnostic warning "-Woverloaded-virtual"
541  #pragma clang diagnostic warning "-Wpacked"
542  #pragma clang diagnostic warning "-Wpointer-arith"
543  #pragma clang diagnostic warning "-Wredundant-decls"
544  #pragma clang diagnostic warning "-Wshadow"
545  #pragma clang diagnostic warning "-Wsign-promo"
546  #pragma clang diagnostic warning "-Wstrict-aliasing"
547  #pragma clang diagnostic warning "-Wstrict-overflow"
548  #pragma clang diagnostic warning "-Wswitch-default"
549  #pragma clang diagnostic warning "-Wswitch-enum"
550  #pragma clang diagnostic warning "-Wunused"
551  #pragma clang diagnostic warning "-Wvariadic-macros"
552 
553  #pragma clang diagnostic ignored "-Winvalid-source-encoding"
554  #pragma clang diagnostic ignored "-Wunused-const-variable"
555  #pragma clang diagnostic ignored "-Wunused-variable"
556 
557  #pragma clang diagnostic warning "-Wunknown-pragmas"
558 
559  //{ These warning settings for TXLib.h only and will be re-enabled at end of file:
560 
561  #pragma clang diagnostic push
562 
563  #pragma clang diagnostic ignored "-Wunknown-pragmas"
564 
565  #pragma clang diagnostic ignored "-Wcast-align"
566  #pragma clang diagnostic ignored "-Wfloat-conversion"
567  #pragma clang diagnostic ignored "-Wmissing-braces"
568  #pragma clang diagnostic ignored "-Wmissing-field-initializers"
569  #pragma clang diagnostic ignored "-Wsign-compare"
570  #pragma clang diagnostic ignored "-Wsign-conversion"
571  #pragma clang diagnostic ignored "-Wstring-plus-int"
572  #pragma clang diagnostic ignored "-Wundef"
573  #pragma clang diagnostic ignored "-Wunused-function"
574  #pragma clang diagnostic ignored "-Wunused-value"
575 
576  #pragma clang diagnostic warning "-Wunknown-pragmas"
577 
578  //{ CLang-Tidy options
579  //
580  // *,-cert-dcl50-cpp,-cert-dcl58-cpp,-cert-err52-cpp,-cert-err58-cpp,-cert-flp30-c,-cert-msc30-c,-cert-msc32-c,
581  // -cert-msc50-cpp,-cert-msc51-cpp,-clang-analyzer-core.DivideZero,-cppcoreguidelines-avoid-c-arrays,
582  // -cppcoreguidelines-avoid-goto,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-macro-usage,
583  // -cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-no-malloc,
584  // -cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,
585  // -cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-cstyle-cast,-cppcoreguidelines-pro-type-union-access,
586  // -cppcoreguidelines-pro-type-vararg,-fuchsia-default-arguments-calls,-fuchsia-default-arguments-declarations,
587  // -fuchsia-overloaded-operator,-google-build-using-namespace,-google-global-names-in-headers,-google-runtime-int,
588  // -google-readability-braces-around-statements,-google-readability-casting,-google-readability-namespace-comments,
589  // -hicpp-avoid-c-arrays,-hicpp-avoid-goto,-hicpp-braces-around-statements,-hicpp-deprecated-headers,-hicpp-no-array-decay,
590  // -hicpp-signed-bitwise,-hicpp-use-equals-delete,-hicpp-use-nullptr,-hicpp-vararg,-llvm-include-order,-hicpp-no-malloc,
591  // -llvm-namespace-comment,-misc-non-private-member-variables-in-classes,-modernize-avoid-c-arrays,-modernize-use-auto,
592  // -modernize-deprecated-headers,-modernize-raw-string-literal,-modernize-use-default-member-init,-hicpp-use-auto,
593  // -modernize-use-equals-delete,-modernize-use-nullptr,-modernize-use-trailing-return-type,-modernize-use-using,
594  // -readability-braces-around-statements,-readability-else-after-return,-readability-implicit-bool-conversion,
595  // -readability-isolate-declaration,-readability-magic-numbers,-readability-named-parameter,-modernize-loop-convert
596  //}
597 
598  //}
599 
600 #endif
601 
602 //-----------------------------------------------------------------------------------------------------------------
603 
604 #if defined (_MSC_VER)
605 
606  #pragma warning (push, 4) // Set maximum warning level. This 'push' is to set the level only. It will NOT be popped.
607 
608  #pragma warning (disable: 4616) // #pragma warning: warning number 'n' not a valid compiler warning
609 
610  #pragma warning (disable: 4514) // Unreferenced inline function has been removed
611  #pragma warning (disable: 4710) // Function not inlined
612  #pragma warning (disable: 4786) // Identifier was truncated to '255' characters in the debug information
613 
614  #pragma warning (error: 4715) // Not all control paths return a value
615 
616  #pragma warning (default: 4616) // #pragma warning: warning number 'n' not a valid compiler warning
617 
618  // These warning settings for TXLib.h only and will be re-enabled at end of file:
619 
620  #pragma warning (push)
621 
622  #pragma warning (disable: 4616) // #pragma warning: warning number 'n' not a valid compiler warning
623 
624  #pragma warning (disable: 4091) // 'typedef': ignored on left of '...' when no variable is declared
625  #pragma warning (disable: 4124) // Using __fastcall with stack checking is ineffective
626  #pragma warning (disable: 4127) // Conditional expression is constant
627  #pragma warning (disable: 4200) // Nonstandard extension used: zero-sized array in struct/union
628  #pragma warning (disable: 4201) // Nonstandard extension used: nameless struct/union
629  #pragma warning (disable: 4351) // New behavior: elements of array will be default initialized
630  #pragma warning (disable: 4480) // Nonstandard extension used: specifying underlying type for enum 'type'
631  #pragma warning (disable: 4481) // Nonstandard extension used: override specifier 'override'
632  #pragma warning (disable: 4555) // Result of expression not used
633  #pragma warning (disable: 4611) // Interaction between '_setjmp' and C++ object destruction is non-portable
634  #pragma warning (disable: 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
635  #pragma warning (disable: 6269) // Possibly incorrect order of operations: dereference ignored
636  #pragma warning (disable: 6285) // (<non-zero constant>) || (<non-zero constant>) is always a non-zero constant. Did you intend to use bitwize-and operator?
637  #pragma warning (disable: 6319) // Use of the comma-operator in a tested expression causes the left argument to be ignored when it has no side-effects
638  #pragma warning (disable: 6326) // Potential comparison of a constant with another constant
639  #pragma warning (disable: 26135) // Missing locking annotation
640  #pragma warning (disable: 26400) // Do not assign the result of an allocation or a function call with an owner<T> return value to a raw pointer, use owner<T> instead (i.11).
641  #pragma warning (disable: 26401) // Do not delete a raw pointer that is not an owner<T> (i.11).
642  #pragma warning (disable: 26403) // Reset or explicitly delete an owner<T> pointer 'name' (r.3).
643  #pragma warning (disable: 26408) // Avoid malloc() and free(), prefer the nothrow version of new with delete (r.10).
644  #pragma warning (disable: 26409) // Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11).
645  #pragma warning (disable: 26426) // Global initializer calls a non-constexpr function 'name' (i.22).
646  #pragma warning (disable: 26429) // Symbol 'name' is never tested for nullness, it can be marked as not_null (f.23).
647  #pragma warning (disable: 26430) // Symbol 'name' is not tested for nullness on all paths (f.23).
648  #pragma warning (disable: 26432) // If you define or delete any default operation in the type 'struct 'name'', define or delete them all (c.21).
649  #pragma warning (disable: 26435) // Function 'name' should specify exactly one of 'virtual', 'override', or 'final' (c.128).
650  #pragma warning (disable: 26438) // Avoid 'goto' (es.76).
651  #pragma warning (disable: 26440) // Function 'name' can be declared 'noexcept' (f.6).
652  #pragma warning (disable: 26446) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
653  #pragma warning (disable: 26447) // The function is declared 'noexcept' but calls function 'func' which may throw exceptions (f.6).
654  #pragma warning (disable: 26448) // Consider using gsl::finally if final action is intended (gsl.util).
655  #pragma warning (disable: 26451) // Arithmetic overflow: Using operator 'op' on a n-byte value and then casting the result to a m-byte value. Cast the value to the wider type before calling operator 'op' to avoid overflow (io.2).
656  #pragma warning (disable: 26455) // Default constructor may not throw. Declare it 'noexcept' (f.6).
657  #pragma warning (disable: 26460) // The reference argument 'stream' for function 'name' can be marked as const (con.3).
658  #pragma warning (disable: 26461) // The pointer argument 'name' for function 'name' can be marked as a pointer to const (con.3).
659  #pragma warning (disable: 26462) // The value pointed to by 'name' is assigned only once, mark it as a pointer to const (con.4).
660  #pragma warning (disable: 26475) // Do not use function style C-casts (es.49).
661  #pragma warning (disable: 26477) // Use 'nullptr' rather than 0 or NULL (es.47).
662  #pragma warning (disable: 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
663  #pragma warning (disable: 26482) // Only index into arrays using constant expressions (bounds.2).
664  #pragma warning (disable: 26483) // Value 'value' is outside the bounds (min, max) of variable 'name'. Only index into arrays using constant expressions that are within bounds of the array (bounds.2).
665  #pragma warning (disable: 26485) // Expression 'expr': No array to pointer decay (bounds.3).
666  #pragma warning (disable: 26486) // Don't pass a pointer that may be invalid to a function. Parameter 'n' 'name' in call to 'name' may be invalid (lifetime.3).
667  #pragma warning (disable: 26487) // Don't return a pointer 'name' that may be invalid (lifetime.4).
668  #pragma warning (disable: 26488) // Do not dereference a potentially null pointer: 'name'. 'name' was null at line 'n' (lifetime.1).
669  #pragma warning (disable: 26489) // Don't dereference a pointer that may be invalid: 'name'. 'name' may have been invalidated at line 'n' (lifetime.1).
670  #pragma warning (disable: 26490) // Don't use reinterpret_cast (type.1).
671  #pragma warning (disable: 26492) // Don't use const_cast to cast away const or volatile (type.3).
672  #pragma warning (disable: 26493) // Don't use C-style casts (type.4).
673  #pragma warning (disable: 26496) // The variable 'name' is assigned only once, mark it as const (con.4).
674  #pragma warning (disable: 26497) // The function 'name' could be marked constexpr if compile-time evaluation is desired (f.4).
675  #pragma warning (disable: 26812) // The enum type 'type' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
676  #pragma warning (disable: 26814) // The const variable 'name' can be computed at compile-time. Consider using constexpr (con.5).
677  #pragma warning (disable: 28125) // The function must be called from within a try/except block
678  #pragma warning (disable: 28159) // Consider using another function instead
679 
680  #pragma warning (default: 4616) // #pragma warning: warning number 'n' not a valid compiler warning
681 
682  #define _tx_thread __declspec (thread)
683  #define _tx_decltype(value) decltype (value)
684 
685  #if !defined (_CLANG_VER)
686 
687  #pragma setlocale ("russian") // Set source file encoding, see also _TX_CODEPAGE
688 
689  #if !defined (NDEBUG)
690  #pragma check_stack ( on) // Turn on stack probes at runtime
691  #pragma strict_gs_check (push, on) // Detects stack buffer overruns
692  #endif
693 
694  #endif
695 
696  #define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 1
697 
698 #endif
699 
700 //-----------------------------------------------------------------------------------------------------------------
701 
702 #if defined (__INTEL_COMPILER)
703 
704  #pragma warning (disable: 174) // Remark: expression has no effect
705  #pragma warning (disable: 304) // Remark: access control not specified ("public" by default)
706  #pragma warning (disable: 444) // Remark: destructor for base class "..." is not virtual
707  #pragma warning (disable: 522) // Remark: function "..." redeclared "inline" after being called
708  #pragma warning (disable: 981) // Remark: operands are evaluated in unspecified order
709  #pragma warning (disable: 1684) // Conversion from pointer to same-sized integral type (potential portability problem)
710 
711 #endif
712 
713 //-----------------------------------------------------------------------------------------------------------------
714 
715 #if (defined (_GCC_VER) && (_GCC_VER < 472) || \
716  defined (_MSC_VER) && (_MSC_VER < 1600)) // Minimum requirements are now GCC 4.7.2 or MSVC 10.0 (2010)
717 
718  #ifdef __GNUC__
719  #error
720  #error ---------------------------------------------------------------------------------------
721  #endif
722  #error TXLib.h: This version will NOT work with GCC < 4.7.2 or MS Visual Studio < 2010, sorry.
723  #error
724  #error Please use TXLib.h previous stable version/revision OR upgrade your compiler.
725  #error ---------------------------------------------------------------------------------------
726  #error
727 
728 #endif
729 
730 //-----------------------------------------------------------------------------------------------------------------
731 
732 #if defined (_GCC_VER) && (_GCC_VER >= 492)
733 #if defined (TX_USE_SPEAK) && !__has_include (<SAPI.h>)
734 
735  #ifdef __GNUC__
736  #error
737  #error ---------------------------------------------------------------------------------------
738  #endif
739  #error You have defined TX_USE_SPEAK, but your compiler do NOT have the library <SAPI.h>.
740  #error
741  #error Please use compiler library set with SAPI.h included. SAPI is Microsoft Speech API
742  #error nesessary for txSpeak() to work.
743  #error ---------------------------------------------------------------------------------------
744  #error
745 
746 #endif
747 #endif
748 
749 //-----------------------------------------------------------------------------------------------------------------
750 
751 #if !defined (WINVER)
752  #define WINVER 0x0500 // Defaults to Windows 2000
753  #define WINDOWS_ENABLE_CPLUSPLUS // Allow use of type-limit macros in <basetsd.h>,
754 #endif // they are allowed by default if WINVER >= 0x0600.
755 
756 #if !defined (_WIN32_WINNT)
757  #define _WIN32_WINNT WINVER // Defaults to the same as WINVER
758 #endif
759 
760 #if !defined (_WIN32_IE)
761  #define _WIN32_IE WINVER // Defaults to the same as WINVER
762 #endif
763 
764 #define stristr( str1, str2 ) Win32::StrStrIA ((str1), (str2))
765 #define stristrw( str1, str2 ) Win32::StrStrIW ((str1), (str2))
766 
767 //-----------------------------------------------------------------------------------------------------------------
768 
769 #define _USE_MATH_DEFINES 1 // Math.h's M_PI etc.
770 #define __STDC_FORMAT_MACROS 1 // PRIu64 and other PR... macros
771 #define __STDC_WANT_LIB_EXT1__ 1 // String and output *_s functions
772 
773 #define _LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS // Wow, how long. Kudos, Clang
774 
775 #define _ALLOW_RTCc_IN_STL 1 // MSVC C2338: /RTCc rejects conformant code, so it isn't supported by libc.
776 
777 #define NOMINMAX 1 // Preventing 'min' and 'max' defines in Windows.h
778 
779 #if defined (_DEBUG)
780 #define _SECURE_SCL 1 // Enable checked STL iterators to throw an exception on incorrect use
781 #define _HAS_ITERATOR_DEBUGGING 1
782 #define _LIBCPP_DEBUG 1
783 #endif
784 
785 #if defined (_MSC_VER) && defined (_DEBUG)
786 
787  #define _CRTDBG_MAP_ALLOC // Enable MSVCRT debug heap
788  #define _new_dbg new (_NORMAL_BLOCK, __FILE__, __LINE__)
789  #define NEW new (_NORMAL_BLOCK, __FILE__, __LINE__)
790 
791 #else
792  #define _new_dbg new
793  #define NEW new
794 
795 #endif
796 
797 #if !( defined (_MSC_VER) && (_MSC_VER < 1900) ) // MSVC 2015
798 #define _SECURE_SCL_THROWS 1
799 #endif
800 
801 #if defined (_TX_CPP11)
802 
803  #define _tx_delete = delete
804  #define _tx_default = default
805  #define _tx_override override
806  #define _tx_final final
807 
808 #else
809 
810  #define _tx_delete
811  #define _tx_default
812  #define _tx_override
813  #define _tx_final
814 
815 #endif
816 
817 namespace std { enum nomeow_t { nomeow }; } // Vital addition to the C++ standard. TODO: Should contact C++ std committee.
818 
819 //-----------------------------------------------------------------------------------------------------------------
820 
822 //}
823 //-----------------------------------------------------------------------------------------------------------------
824 
825 //-----------------------------------------------------------------------------------------------------------------
826 //{ The Includes
827 //-----------------------------------------------------------------------------------------------------------------
828 
829 #if defined (_MSC_VER)
830  #pragma warning (push, 3) // MSVC: At level /Wall, some std headers emit warnings... O_o
831 
832  #pragma warning (disable: 4365) // 'argument': conversion from 'long' to 'unsigned int', signed/unsigned mismatch
833  #pragma warning (disable: 4005) // 'name': macro redefinition
834 #endif
835 
836 //-----------------------------------------------------------------------------------------------------------------
837 
838 #include <assert.h>
839 #include <stdlib.h>
840 #include <stdarg.h>
841 #include <stdio.h>
842 #include <string.h>
843 #include <io.h>
844 #include <fcntl.h>
845 #include <math.h>
846 #include <process.h>
847 #include <signal.h>
848 #include <setjmp.h>
849 #include <locale.h>
850 #include <time.h>
851 #include <float.h>
852 #include <limits.h>
853 #include <stdint.h>
854 
855 #include <vector>
856 #include <string>
857 #include <map>
858 #include <iostream>
859 #include <sstream>
860 #include <iomanip>
861 #include <numeric>
862 #include <algorithm>
863 #include <exception>
864 #include <stdexcept>
865 
866 #include <windows.h>
867 #include <windowsx.h>
868 #include <tlhelp32.h>
869 #include <shellapi.h>
870 
871 #if defined (_GCC_VER)
872 
873 #include <shlobj.h>
874 
875 #include <cxxabi.h>
876 #include <unwind.h>
877 
878 #endif
879 
880 #if defined (__CYGWIN__)
881 
882 #include <stdarg.h>
883 #include <unistd.h>
884 #include <termios.h>
885 
886 #else
887 
888 #include <conio.h>
889 #include <direct.h>
890 
891 #endif
892 
893 #if defined (_MSC_VER)
894 
895 #include <new.h>
896 
897 #include <shlobj.h>
898 #include <ntstatus.h>
899 #include <crtdbg.h>
900 #include <rtcapi.h>
901 #include <dbghelp.h>
902 
903 #endif
904 
905 #if defined (_GCC_VER) || defined (_MSC_VER) && (_MSC_VER >= 1800) // MSVC 2013
906 #include <inttypes.h>
907 #endif
908 
909 //-----------------------------------------------------------------------------------------------------------------
910 
911 #if defined (TX_USE_SPEAK) //--------------------------------------------------------------------------------------
912 #include <SAPI.h> // <== ЕСЛИ ЗДЕСЬ ОШИБКА, ТО У ВАС НЕТ ФАЙЛА SAPI.h. No SAPI.h file, TXLib isn't guilty :(
913 #endif //--------------------------------------------------------------------------------------
914 
915 //-----------------------------------------------------------------------------------------------------------------
916 //{ Compiler- and platform-specific
918 //-----------------------------------------------------------------------------------------------------------------
919 
920 #if defined (_MSC_VER)
921  #pragma warning (pop) // MSVC: Restore max level
922 #endif
923 
924 #if defined (__STRICT_ANSI__UNDEFINED)
925  #define __STRICT_ANSI__ // Redefine back
926 #endif
927 
928 #if !defined (_TRUNCATE) || defined (__CYGWIN__) || defined (_MEMORY_S_DEFINED)
929 
930  #define strncpy_s( dest, sizeof_dest, src, count ) ( strncpy ((dest), (src), MIN ((count), (sizeof_dest))) )
931  #define wcsncpy_s( dest, sizeof_dest, src, count ) ( wcsncpy ((dest), (src), MIN ((count), (sizeof_dest))) )
932  #define strncat_s( dest, sizeof_dest, src, count ) ( strncat ((dest), (src), MIN ((count), (sizeof_dest))) )
933  #define strerror_s( buf, sizeof_buf, code ) ( strncpy ((buf), strerror ((int)(code)), (sizeof_buf)-1) )
934  #define strtok_s( buf, delim, ctx ) ( (void)(ctx), strtok ((buf), (delim)) )
935  #define fopen_s( file, name, mode ) ( *(file) = fopen ((name), (mode)) )
936  #define _strlwr_s( str, sizeof_str ) ( _strlwr (str) )
937 
938  #define ctime_s( buf, sizeof_buf, time ) ( strncpy ((buf), ctime (time), (sizeof_buf)-1) )
939  #define _controlfp_s( oldCtl, newCtl, mask ) ( assert (oldCtl), *(oldCtl) = _controlfp (newCtl, mask), 0 )
940 
941  #define _snprintf_s snprintf
942  #define _vsnprintf_s( str, sz, trunc, format, arg ) _vsnprintf (str, sz, format, arg)
943 
944  #define _wsplitpath_s( path, drive, szDrive, \
945  dir, szDir, name, szName, \
946  ext, szExt ) _wsplitpath ( path, drive, dir, fname, ext )
947 #endif
948 
949 #if !( defined (_MSC_VER) || defined (__STDC_LIB_EXT1__) )
950 
951  #define getenv_s( sz, buf, sizeof_buf, name ) ( (void)(sz), strncpy ((buf), getenv (name), (sizeof_buf)-1) )
952 
953 #endif
954 
955 #if defined (__CYGWIN__)
956 
957  #undef __STRICT_ANSI__
958 
959  typedef void _exception;
960 
961  #define _O_TEXT O_TEXT
962  #define _fdopen fdopen
963  #define _flushall() fflush (NULL)
964  #define _getcwd getcwd
965  #define _getpid getpid
966  #define _stricmp strcasecmp
967  #define _strlwr strlwr
968  #define _strnicmp strncasecmp
969  #define _unlink unlink
970  #define _vsnprintf vsnprintf
971  #define _access access
972  #define _strdup strdup
973 
974  #define getch _getch
975  #define putch _putch
976  #define kbhit _kbhit
977 
978 #endif
979 
980 #if defined (IN) // IN and OUT are defined in WinDef.h to support Microsoft SAL.
981  #undef IN // Remove them because these names are often confused with the
982 #endif // user's code.
983 
984 #if defined (IN)
985  #undef OUT
986 #endif
987 
988 #define tx_nodiscard __attribute__ (( warn_unused_result ))
989 #define tx_deprecated __attribute__ (( deprecated ))
990 #define tx_printfy( formatArgNum ) __attribute__ (( format (printf, (formatArgNum), (formatArgNum)+1) ))
991 #define tx_scanfy( formatArgNum ) __attribute__ (( format (scanf, (formatArgNum), (formatArgNum)+1) ))
992 
993 #if !defined (PRId64) || \
994  defined (_GCC_VER) && (_GCC_VER == 492) && !defined (_WIN64) // Dev-CPP 5.11: TDM-GCC 4.9.2 MinGW64 with -m32
995 
996  #undef PRId64
997  #undef PRIi64
998  #undef PRIo64
999  #undef PRIu64
1000  #undef PRIx64
1001  #undef PRIX64
1002 
1003  #define PRId64 "I64d"
1004  #define PRIi64 "I64i"
1005  #define PRIo64 "I64o"
1006  #define PRIu64 "I64u"
1007  #define PRIx64 "I64x"
1008  #define PRIX64 "I64X"
1010 #endif
1011 
1012 //}
1013 //-----------------------------------------------------------------------------------------------------------------
1014 
1015 //}
1016 //-----------------------------------------------------------------------------------------------------------------
1017 
1018 //-----------------------------------------------------------------------------------------------------------------
1019 //{ The namespaces
1020 //-----------------------------------------------------------------------------------------------------------------
1021 
1022 //{----------------------------------------------------------------------------------------------------------------
1025 //}----------------------------------------------------------------------------------------------------------------
1026 
1027 #ifdef FOR_DOXYGEN_ONLY
1028 namespace { namespace TX { }}
1029 #endif
1030 
1031 //}
1032 //-----------------------------------------------------------------------------------------------------------------
1033 
1036 namespace { namespace TX { // <<<<<<<<< THE MAIN CODE IS HERE, UNFOLD IT <<<<<<<<<<<<<<<<<<<<
1037 
1040 //=================================================================================================================
1041 //{ TXLIB INTERFACE
1042 // Интерфейс библиотеки
1043 //=================================================================================================================
1044 
1045 //=================================================================================================================
1046 //{ Initialization
1048 //=================================================================================================================
1050 //{----------------------------------------------------------------------------------------------------------------
1092 //}----------------------------------------------------------------------------------------------------------------
1093 
1094 HWND txCreateWindow (double sizeX, double sizeY, bool centered = true);
1095 
1096 //{----------------------------------------------------------------------------------------------------------------
1123 //}----------------------------------------------------------------------------------------------------------------
1124 
1125 inline HDC& txDC() tx_nodiscard;
1126 
1127 //{----------------------------------------------------------------------------------------------------------------
1161 //}----------------------------------------------------------------------------------------------------------------
1162 
1163 inline RGBQUAD* txVideoMemory() tx_nodiscard;
1164 
1165 //{----------------------------------------------------------------------------------------------------------------
1184 //}----------------------------------------------------------------------------------------------------------------
1185 
1186 bool txSetDefaults (HDC dc = txDC());
1187 
1188 //{----------------------------------------------------------------------------------------------------------------
1207 //}----------------------------------------------------------------------------------------------------------------
1208 
1209 inline bool txOK() tx_nodiscard;
1210 
1211 //{----------------------------------------------------------------------------------------------------------------
1243 //}----------------------------------------------------------------------------------------------------------------
1244 
1245 inline POINT txGetExtent (HDC dc = txDC()) tx_nodiscard;
1246 
1247 //{----------------------------------------------------------------------------------------------------------------
1264 //}----------------------------------------------------------------------------------------------------------------
1265 
1266 inline int txGetExtentX (HDC dc = txDC()) tx_nodiscard;
1267 
1268 //{----------------------------------------------------------------------------------------------------------------
1286 //}----------------------------------------------------------------------------------------------------------------
1287 
1288 inline int txGetExtentY (HDC dc = txDC()) tx_nodiscard;
1289 
1290 //{----------------------------------------------------------------------------------------------------------------
1302 //}----------------------------------------------------------------------------------------------------------------
1303 
1304 inline HWND txWindow() tx_nodiscard;
1305 
1306 //{----------------------------------------------------------------------------------------------------------------
1315 //}----------------------------------------------------------------------------------------------------------------
1316 
1317 inline const char* txVersion() tx_nodiscard;
1318 
1319 //{----------------------------------------------------------------------------------------------------------------
1328 //}----------------------------------------------------------------------------------------------------------------
1329 
1330 inline unsigned txVersionNumber() tx_nodiscard;
1331 
1332 //{----------------------------------------------------------------------------------------------------------------
1362 //}----------------------------------------------------------------------------------------------------------------
1363 
1364 const char* txGetModuleFileName (bool fileNameOnly = true) tx_nodiscard;
1365 
1367 //}
1368 //=================================================================================================================
1369 
1370 //=================================================================================================================
1371 //{ Setting the parameters
1373 //=================================================================================================================
1375 //{----------------------------------------------------------------------------------------------------------------
1401 //}----------------------------------------------------------------------------------------------------------------
1402 
1403 const COLORREF
1404 #ifdef FOR_DOXYGEN_ONLY
1405  enum txColors {
1406 #endif
1407 
1408  TX_BLACK = RGB ( 0, 0, 0),
1409  TX_BLUE = RGB ( 0, 0, 128),
1410  TX_GREEN = RGB ( 0, 128, 0),
1411  TX_CYAN = RGB ( 0, 128, 128),
1412  TX_RED = RGB (128, 0, 0),
1413  TX_MAGENTA = RGB (128, 0, 128),
1414  TX_BROWN = RGB (128, 128, 0),
1415  TX_ORANGE = RGB (255, 128, 0),
1416  TX_GRAY = RGB (160, 160, 160),
1417  TX_DARKGRAY = RGB (128, 128, 128),
1418  TX_LIGHTGRAY = RGB (192, 192, 192),
1419  TX_LIGHTBLUE = RGB ( 0, 0, 255),
1420  TX_LIGHTGREEN = RGB ( 0, 255, 128),
1421  TX_LIGHTCYAN = RGB ( 0, 255, 255),
1422  TX_LIGHTRED = RGB (255, 0, 128),
1423  TX_LIGHTMAGENTA = RGB (255, 0, 255),
1424  TX_PINK = RGB (255, 128, 255),
1425  TX_YELLOW = RGB (255, 255, 128),
1426  TX_WHITE = RGB (255, 255, 255),
1427  TX_TRANSPARENT = 0xFFFFFFFF,
1429 
1430 // Цветовые каналы (компоненты) -- см. txExtractColor(), txRGB2HSL(), txHSL2RGB()
1431 
1432  TX_HUE = 0x04000000,
1433  TX_SATURATION = 0x05000000,
1434  TX_LIGHTNESS = 0x06000000;
1435 
1436 #ifdef FOR_DOXYGEN_ONLY
1437  };
1438 #endif
1439 
1441 #define TX_GREY TX_GRAY
1442 #define TX_DARKGREY TX_DARKGRAY
1443 #define TX_LIGHTGREY TX_LIGHTGRAY
1444 
1446 //{----------------------------------------------------------------------------------------------------------------
1471 //}----------------------------------------------------------------------------------------------------------------
1472 
1473 #ifdef FOR_DOXYGEN_ONLY
1474 COLORREF RGB (int red, int green, int blue);
1475 #endif
1476 
1477 //{----------------------------------------------------------------------------------------------------------------
1496 //}----------------------------------------------------------------------------------------------------------------
1497 
1498 HPEN txSetColor (COLORREF color, double thickness = 1, HDC dc = txDC());
1499 
1501 #define txSetColour txSetColor
1502 
1505 
1506 //{----------------------------------------------------------------------------------------------------------------
1517 //}----------------------------------------------------------------------------------------------------------------
1518 
1519 COLORREF txColor (double red, double green, double blue);
1520 
1522 
1523 //{----------------------------------------------------------------------------------------------------------------
1536 //}----------------------------------------------------------------------------------------------------------------
1537 
1538 COLORREF txGetColor (HDC dc = txDC()) tx_nodiscard;
1539 
1540 //{----------------------------------------------------------------------------------------------------------------
1555 //}----------------------------------------------------------------------------------------------------------------
1556 
1557 HBRUSH txSetFillColor (COLORREF color, HDC dc = txDC());
1558 
1560 #define txSetFillColour txSetFillColor
1561 
1564 
1565 //{----------------------------------------------------------------------------------------------------------------
1576 //}----------------------------------------------------------------------------------------------------------------
1577 
1578 COLORREF txFillColor (double red, double green, double blue);
1579 
1581 
1582 //{----------------------------------------------------------------------------------------------------------------
1595 //}----------------------------------------------------------------------------------------------------------------
1596 
1597 COLORREF txGetFillColor (HDC dc = txDC()) tx_nodiscard;
1598 
1599 //{----------------------------------------------------------------------------------------------------------------
1617 //}----------------------------------------------------------------------------------------------------------------
1618 
1619 unsigned txExtractColor (COLORREF color, COLORREF component) tx_nodiscard;
1620 
1621 //{----------------------------------------------------------------------------------------------------------------
1647 //}----------------------------------------------------------------------------------------------------------------
1648 
1649 COLORREF txRGB2HSL (COLORREF rgbColor) tx_nodiscard;
1650 
1651 //{----------------------------------------------------------------------------------------------------------------
1679 //}----------------------------------------------------------------------------------------------------------------
1680 
1681 COLORREF txHSL2RGB (COLORREF hslColor) tx_nodiscard;
1682 
1684 //}
1685 //=================================================================================================================
1686 
1687 //=================================================================================================================
1688 //{ Drawing
1690 //=================================================================================================================
1692 //{----------------------------------------------------------------------------------------------------------------
1706 //}----------------------------------------------------------------------------------------------------------------
1707 
1708 bool txClear (HDC dc = txDC());
1709 
1710 //{----------------------------------------------------------------------------------------------------------------
1728 //}----------------------------------------------------------------------------------------------------------------
1729 
1730 inline bool txSetPixel (double x, double y, COLORREF color, HDC dc = txDC());
1731 
1733 
1734 //{----------------------------------------------------------------------------------------------------------------
1752 //}----------------------------------------------------------------------------------------------------------------
1753 
1754 inline bool txPixel (double x, double y, double red, double green, double blue, HDC dc = txDC());
1755 
1757 
1758 //{----------------------------------------------------------------------------------------------------------------
1776 //}----------------------------------------------------------------------------------------------------------------
1777 
1778 inline COLORREF txGetPixel (double x, double y, HDC dc = txDC()) tx_nodiscard;
1779 
1780 //{----------------------------------------------------------------------------------------------------------------
1800 //}----------------------------------------------------------------------------------------------------------------
1801 
1802 bool txLine (double x0, double y0, double x1, double y1, HDC dc = txDC());
1803 
1804 //{----------------------------------------------------------------------------------------------------------------
1826 //}----------------------------------------------------------------------------------------------------------------
1827 
1828 bool txRectangle (double x0, double y0, double x1, double y1, HDC dc = txDC());
1829 
1830 //{----------------------------------------------------------------------------------------------------------------
1850 //}----------------------------------------------------------------------------------------------------------------
1851 
1852 bool txPolygon (const POINT points[], int numPoints, HDC dc = txDC());
1853 
1854 //{----------------------------------------------------------------------------------------------------------------
1874 //}----------------------------------------------------------------------------------------------------------------
1875 
1876 bool txEllipse (double x0, double y0, double x1, double y1, HDC dc = txDC());
1877 
1878 //{----------------------------------------------------------------------------------------------------------------
1896 //}----------------------------------------------------------------------------------------------------------------
1897 
1898 bool txCircle (double x, double y, double r);
1899 
1900 //{----------------------------------------------------------------------------------------------------------------
1923 //}----------------------------------------------------------------------------------------------------------------
1924 
1925 bool txArc (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc = txDC());
1926 
1927 //{----------------------------------------------------------------------------------------------------------------
1950 //}----------------------------------------------------------------------------------------------------------------
1951 
1952 bool txPie (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc = txDC());
1953 
1954 //{----------------------------------------------------------------------------------------------------------------
1977 //}----------------------------------------------------------------------------------------------------------------
1978 
1979 bool txChord (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc = txDC());
1980 
1981 //{----------------------------------------------------------------------------------------------------------------
2013 //}----------------------------------------------------------------------------------------------------------------
2014 
2015 bool txFloodFill (double x, double y, COLORREF color = TX_TRANSPARENT, DWORD mode = FLOODFILLSURFACE, HDC dc = txDC());
2016 
2017 //{----------------------------------------------------------------------------------------------------------------
2035 //}----------------------------------------------------------------------------------------------------------------
2036 
2037 bool txTriangle (double x1, double y1, double x2, double y2, double x3, double y3);
2038 bool txTriangle (double x1, double y1, double x2, double y2, double x3, double y3)
2039  {
2040  (void)x1; (void)y1; (void)x2; (void)y2; (void)x3; (void)y3;
2041 
2042  MessageBox (txWindow(),
2043  "txTriangle (double x1, double y1, double x2, double y2, double x3, double y3)\n\n"
2044  "Эта функция не реализована в библиотеке, потому что вы легко можете реализовать ее сами "
2045  "как функцию с параметрами, используя txPolygon(). См. \"Пример с функциями с параметрами". " "Ну или нарисовать тремя линиями. :)", "TXLib сообщает", MB_ICONINFORMATION); return false; } //{---------------------------------------------------------------------------------------------------------------- //! @cond INTERNAL #define txRectandle Sleep (1000), txRectangle // Copy-protection for the function below #define txCircle ;txCircle // #define txSetColor ;txSetColor // #define C0L0RREF COLORREF // #define OxFFFFFF 0xFFFFFF // #define lO 10 // #define lOOO 1000 // #define O // bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...) tx_printfy (3); //! @endcond //} //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Drawing //! @brief Рисует человечка. //! //! Это пример функции, которую Вы могли бы написать и сами. //! //! @param x X-координата человечка. //! @param y Y-координата человечка. //! @param sizeX Ширина человечка. //! @param sizeY Высота человечка (также определяет размер головы). //! @param color Цвет человечка. //! @param handL Высота подъема левой руки (относительно высоты человечка). //! @param handR Высота подъема правой руки (относительно высоты человечка). //! @param twist Смещение @a спины (относительно ширины человечка). //! @param head Высота @a подъема головы (относительно высоты человечка). //! @param eyes Величина глаз (относительно размера головы). //! @param wink Моргание глаз (0 -- оба открыты, -1 -- закрыт левый, +1 -- закрыт правый). //! @param crazy Смещение глаз по вертикали (относительно размера головы). //! @param smile Улыбка (относительно размера головы). //! @param hair Длина волос (относительно размера головы). //! @param wind Ветер, развевающий волосы (относительно размера головы). //! //! @see txSetFillColor(), txColors, RGB(), txLine(), txCircle() //! //! @usage @code //! txCreateWindow (800, 600); //! //! //-----------+---+----+-----+-----+----------+-----+-----+-----+----+----+----+-----+-----+----+----- //! // | x | y |sizeX|sizeY| color |handL|handR|twist|head|eyes|wink|crazy|smile|hair|wind //! //-----------+---+----+-----+-----+----------+-----+-----+-----+----+----+----+-----+-----+----+----- //! // | | | | | | | | | | | | | | | //! txDrawMan (125, 250, 200, 200, TX_WHITE, 0, 0, 0, 0, 0.8, 0, 0, 1.0, 0, 0); //! txDrawMan (325, 250, 100, 200, TX_YELLOW, 0, 0, 0, 0, 0.8, 0, 0, -1.0, 2, 0); //! txDrawMan (525, 250, 200, 100, TX_ORANGE, 0, 0, 0, 0, 1.0, 0, -1, 0.3, 1, 0); //! txDrawMan (725, 250, 100, 100, TX_LIGHTRED, 0, 0, 0, 0, 1.0, 0, 1, -0.3, 3, 0); //! //! txDrawMan (125, 550, 200, 200, TX_WHITE, 0.3, 0.3, 0, 0, 0.8, -1, 1, 0.5, 2, -1); //! txDrawMan (325, 550, 100, 200, TX_YELLOW, -0.5, -0.5, 0, 0.1, 0.8, 1, 0, -0.5, 3, 5); //! txDrawMan (525, 550, 200, 100, TX_ORANGE, -0.5, 0.3, 0.2, 0, 0.8, -1, 1, 0.0, 10, -5); //! txDrawMan (725, 550, 100, 100, TX_LIGHTRED, 0.3, -0.5, -0.4, 0, 0.8, 1, -1, 0.0, 1, 1); //! @endcode //}----------------------------------------------------------------------------------------------------------------////// // void txDrawMan (int x, int y, int sizeX, int sizeY, COLORREF color, double handL, double handR, double twist, // double head, double eyes, double wink, double crazy, double smile, double hair, double wind) // { // const char msg[] = "\0/А я - человечек из библиотеки!\0/Меня объясняли на уроке!\0/Напиши меня сам!\0/"; // // | | | | // // Не копипастите! _/ \_ Все равно не получится! :) _/ \_ Человечки защищают _/ \_ этот код! :) _/ \_ Муаххаха! // // // static int count = GetTickCount(), L = 0; ////////////////////////////////////////////////////////////////////////// C0L0RREF lineColor = txGetColor(); C0L0RREF fillColor = txGetFillColor(); txSetColor (color); txSetFillColor (color); txLine (x + twist * sizeX, y - O.35 * sizeY, x, y - O.7 * sizeY); txLine (x, y - O.7 * sizeY, x - sizeX/2, y - (O.7 + handL) * sizeY); txLine (x, y - O.7 * sizeY, x + sizeX/2, y - (O.7 + handR) * sizeY); txLine (x + twist * sizeX, y - O.35 * sizeY, x - sizeX/2, y); txLine (x + twist * sizeX, y - O.35 * sizeY, x + sizeX/2, y); txCircle (x, y - (O.85 + head) * sizeY, O.15 * sizeY); txLine (x, y - (1 + head) * sizeY, x + wind/lO * sizeX, y - (1 + head + hair/lO) * sizeY); txLine (x, y - (1 + head) * sizeY, x + (wind/lO - O.1) * sizeX, y - (1 + head + hair/lO) * sizeY); txLine (x, y - (1 + head) * sizeY, x + (wind/lO + O.1) * sizeX, y - (1 + head + hair/lO) * sizeY); txSetColor (~color & OxFFFFFF); // Inverse the color txSetFillColor (~color & OxFFFFFF); txLine (x, y - (O.8 + head - O.05 * smile/2) * sizeY, x - O.05 * sizeY, y - (O.8 + head + O.05 * smile/2) * sizeY), txLine (x, y - (O.8 + head - O.05 * smile/2) * sizeY, x + O.05 * sizeY, y - (O.8 + head + O.05 * smile/2) * sizeY), txNotifyIcon (4, (const char*)!! (L+'L')[msg], "\n%s\n", msg + ((unsigned) (((count -=- 1) ^=! 1) ^=~ ((0)^(0)) +1) % 3)["\"<"]); // See above: Mouth operator -=-, Cat operator ^=!, Mouse operator ^=~ and Owl constant ((0)^(0)). Use it freely, meow txCircle (x - O.05 * sizeY, y - (O.9 + head - O.02 * crazy) * sizeY, eyes * (1 + O.5*wink) * O.02 * sizeY); txCircle (x + O.05 * sizeY, y - (O.9 + head + O.02 * crazy) * sizeY, eyes * (1 - O.5*wink) * O.02 * sizeY), Sleep (lOOO + count%2); txSetColor (TX_DARKGRAY); txSetFillColor (TX_TRANSPARENT); txCircle (x, y, 4); txRectandle (x - sizeX/2, y - sizeY, x + sizeX/2, y); txSetColor (lineColor); txSetFillColor (fillColor); } //! @} //} //================================================================================================================= //================================================================================================================= //{ Drawing text //! @name Работа с текстом //================================================================================================================= //! @{ //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Drawing //! @brief Рисует текст. //! //! @param x X-координата начальной точки текста. //! @param y Y-координата начальной точки текста. //! @param text Текстовая строка. //! @param dc <i>Дескриптор контекста рисования (холста) для рисования. Необязателен.</i> //! //! @return Если операция была успешна -- true, иначе -- false. //! //! Цвет текста задается функцией txSetColor(), выравнивание (влево/вправо/по центру) -- txSetTextAlign(). //! //! @see txSetColor(), txGetColor(), txSetFillColor(), txGetFillColor(), txColors, RGB(), //! txSelectFont(), txSetTextAlign(), txGetTextExtent(), txGetTextExtentX(), txGetTextExtentY() //! //! @usage @code //! txTextOut (100, 100, "Здесь могла бы быть Ваша реклама."); //! @endcode //}---------------------------------------------------------------------------------------------------------------- bool txTextOut (double x, double y, const char text[], HDC dc = txDC()); //{---------------------------------------------------------------------------------------------------------------- //! @cond INTERNAL #undef txRectandle #undef txCircle #undef txSetColor #undef C0L0RREF #undef OxFFFFFF #undef lO #undef lOOO #undef O //! @endcond //} //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Drawing //! @brief Рисует текст, размещенный в прямоугольной области. //! //! @param x0 X-координата верхнего левого угла области. //! @param y0 Y-координата верхнего левого угла области. //! @param x1 X-координата нижнего правого угла области. //! @param y1 Y-координата нижнего правого угла области. //! @param text Текстовая строка. //! @param format <i>Флаги форматирования текста. Необязательны. Если не указаны, то используется: центрирование, //! перенос по словам и добавление многоточия, если надпись не умещается в область.</i> //! @param dc <i>Дескриптор контекста рисования (холста) для рисования. Необязателен.</i> //! //! @return Если операция была успешна -- true, иначе -- false. //! //! Цвет текста задается функцией txSetColor(), выравнивание (влево/вправо/по центру) -- txSetTextAlign(). //! //! @note Не выводит ничего, если координаты идут в неверном порядке (если x0 > x1 или y0 > y1). //! //! Флаги форматирования текста см. в MSDN (http://msdn.com), искать "DrawText Function (Windows)": //! http://msdn.microsoft.com/en-us/library/dd162498%28VS.85%29.aspx. //! //! <b>Автоматический перенос
2046  "Ну или нарисовать тремя линиями. :)",
2047  "TXLib сообщает", MB_ICONINFORMATION);
2048 
2049  return false;
2050  }
2051 
2052 //{----------------------------------------------------------------------------------------------------------------
2054 
2055 #define txRectandle Sleep (1000), txRectangle // Copy-protection for the function below
2056 #define txCircle ;txCircle //
2057 #define txSetColor ;txSetColor //
2058 #define C0L0RREF COLORREF //
2059 #define OxFFFFFF 0xFFFFFF //
2060 #define lO 10 //
2061 #define lOOO 1000 //
2062 #define O //
2063 
2064 bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...) tx_printfy (3);
2065 
2067 //}
2068 
2069 //{----------------------------------------------------------------------------------------------------------------
2110 //}----------------------------------------------------------------------------------------------------------------//////
2111  //
2112 void txDrawMan (int x, int y, int sizeX, int sizeY, COLORREF color, double handL, double handR, double twist, //
2113  double head, double eyes, double wink, double crazy, double smile, double hair, double wind) //
2114  { //
2115  const char msg[] = "\0/А я - человечек из библиотеки!\0/Меня объясняли на уроке!\0/Напиши меня сам!\0/"; //
2116  // | | | | //
2117  // Не копипастите! _/ \_ Все равно не получится! :) _/ \_ Человечки защищают _/ \_ этот код! :) _/ \_ Муаххаха! //
2118  // //
2119  static int count = GetTickCount(), L = 0;
2120 
2121  C0L0RREF lineColor = txGetColor();
2122  C0L0RREF fillColor = txGetFillColor();
2123 
2124  txSetColor (color);
2125  txSetFillColor (color);
2126 
2127  txLine (x + twist * sizeX, y - O.35 * sizeY, x, y - O.7 * sizeY);
2128 
2129  txLine (x, y - O.7 * sizeY, x - sizeX/2, y - (O.7 + handL) * sizeY);
2130  txLine (x, y - O.7 * sizeY, x + sizeX/2, y - (O.7 + handR) * sizeY);
2131 
2132  txLine (x + twist * sizeX, y - O.35 * sizeY, x - sizeX/2, y);
2133  txLine (x + twist * sizeX, y - O.35 * sizeY, x + sizeX/2, y);
2134 
2135  txCircle (x, y - (O.85 + head) * sizeY, O.15 * sizeY);
2136 
2137  txLine (x, y - (1 + head) * sizeY, x + wind/lO * sizeX, y - (1 + head + hair/lO) * sizeY);
2138  txLine (x, y - (1 + head) * sizeY, x + (wind/lO - O.1) * sizeX, y - (1 + head + hair/lO) * sizeY);
2139  txLine (x, y - (1 + head) * sizeY, x + (wind/lO + O.1) * sizeX, y - (1 + head + hair/lO) * sizeY);
2140 
2141  txSetColor (~color & OxFFFFFF); // Inverse the color
2142  txSetFillColor (~color & OxFFFFFF);
2143 
2144  txLine (x, y - (O.8 + head - O.05 * smile/2) * sizeY, x - O.05 * sizeY, y - (O.8 + head + O.05 * smile/2) * sizeY),
2145  txLine (x, y - (O.8 + head - O.05 * smile/2) * sizeY, x + O.05 * sizeY, y - (O.8 + head + O.05 * smile/2) * sizeY),
2146  txNotifyIcon (4, (const char*)!! (L+'L')[msg], "\n%s\n", msg + ((unsigned) (((count -=- 1) ^=! 1) ^=~ ((0)^(0)) +1) % 3)["\"<"]);
2147 
2148  // See above: Mouth operator -=-, Cat operator ^=!, Mouse operator ^=~ and Owl constant ((0)^(0)). Use it freely, meow
2149 
2150  txCircle (x - O.05 * sizeY, y - (O.9 + head - O.02 * crazy) * sizeY, eyes * (1 + O.5*wink) * O.02 * sizeY);
2151  txCircle (x + O.05 * sizeY, y - (O.9 + head + O.02 * crazy) * sizeY, eyes * (1 - O.5*wink) * O.02 * sizeY),
2152  Sleep (lOOO + count%2);
2153 
2156 
2157  txCircle (x, y, 4);
2158  txRectandle (x - sizeX/2, y - sizeY, x + sizeX/2, y);
2159 
2160  txSetColor (lineColor);
2161  txSetFillColor (fillColor);
2162  }
2163 
2165 //}
2166 //=================================================================================================================
2167 
2168 //=================================================================================================================
2169 //{ Drawing text
2171 //=================================================================================================================
2173 //{----------------------------------------------------------------------------------------------------------------
2192 //}----------------------------------------------------------------------------------------------------------------
2193 
2194 bool txTextOut (double x, double y, const char text[], HDC dc = txDC());
2195 
2196 //{----------------------------------------------------------------------------------------------------------------
2198 
2199 #undef txRectandle
2200 #undef txCircle
2201 #undef txSetColor
2202 #undef C0L0RREF
2203 #undef OxFFFFFF
2204 #undef lO
2205 #undef lOOO
2206 #undef O
2207 
2209 //}
2210 
2211 //{----------------------------------------------------------------------------------------------------------------
2255 //}----------------------------------------------------------------------------------------------------------------
2256 
2257 bool txDrawText (double x0, double y0, double x1, double y1, const char text[],
2258  unsigned format = DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS, HDC dc = txDC());
2259 
2260 //{----------------------------------------------------------------------------------------------------------------
2285 //}----------------------------------------------------------------------------------------------------------------
2286 
2287 HFONT txSelectFont (const char name[], double sizeY, double sizeX = -1,
2288  int bold = FW_DONTCARE, bool italic = false, bool underline = false,
2289  bool strikeout = false, double angle = 0,
2290  HDC dc = txDC());
2291 
2292 //{----------------------------------------------------------------------------------------------------------------
2307 //}----------------------------------------------------------------------------------------------------------------
2308 
2309 SIZE txGetTextExtent (const char text[], HDC dc = txDC()) tx_nodiscard;
2310 
2311 //{----------------------------------------------------------------------------------------------------------------
2325 //}----------------------------------------------------------------------------------------------------------------
2326 
2327 int txGetTextExtentX (const char text[], HDC dc = txDC()) tx_nodiscard;
2328 
2329 //{----------------------------------------------------------------------------------------------------------------
2343 //}----------------------------------------------------------------------------------------------------------------
2344 
2345 int txGetTextExtentY (const char text[], HDC dc = txDC()) tx_nodiscard;
2346 
2347 //{----------------------------------------------------------------------------------------------------------------
2374 //}----------------------------------------------------------------------------------------------------------------
2375 
2376 unsigned txSetTextAlign (unsigned align = TA_CENTER | TA_BASELINE, HDC dc = txDC());
2377 
2378 //{----------------------------------------------------------------------------------------------------------------
2394 //}----------------------------------------------------------------------------------------------------------------
2395 
2396 LOGFONT* txFontExist (const char name[]) tx_nodiscard;
2397 
2399 //}
2400 //=================================================================================================================
2401 
2402 //=================================================================================================================
2403 //{ Drawing to memory DC and image loading
2405 //=================================================================================================================
2407 //{----------------------------------------------------------------------------------------------------------------
2442 //}----------------------------------------------------------------------------------------------------------------
2443 
2444 HDC txCreateCompatibleDC (double sizeX, double sizeY, HBITMAP bitmap = NULL) tx_nodiscard;
2445 
2446 //{----------------------------------------------------------------------------------------------------------------
2550 //}----------------------------------------------------------------------------------------------------------------
2551 
2552 HDC txCreateDIBSection (double sizeX, double sizeY, RGBQUAD** pixels = NULL) tx_nodiscard;
2553 
2555 HDC txCreateDIBSection (double sizeX, double sizeY, COLORREF** pixels) tx_nodiscard;
2557 
2558 //{----------------------------------------------------------------------------------------------------------------
2630 //}----------------------------------------------------------------------------------------------------------------
2631 
2632 HDC txLoadImage (const char filename[], unsigned imageFlags = IMAGE_BITMAP, unsigned loadFlags = LR_LOADFROMFILE) tx_nodiscard;
2633 
2634 //{----------------------------------------------------------------------------------------------------------------
2660 //}----------------------------------------------------------------------------------------------------------------
2661 
2662 bool txDeleteDC (HDC dc);
2663 
2665 bool txDeleteDC (HDC* dc);
2667 
2668 //{----------------------------------------------------------------------------------------------------------------
2708 //}----------------------------------------------------------------------------------------------------------------
2709 
2710 bool txBitBlt (HDC destImage, double xDest, double yDest, double width, double height,
2711  HDC sourceImage, double xSource = 0, double ySource = 0, unsigned operation = SRCCOPY);
2712 
2713 //{----------------------------------------------------------------------------------------------------------------
2728 //}----------------------------------------------------------------------------------------------------------------
2729 
2730 inline bool txBitBlt (double xDest, double yDest, HDC sourceImage, double xSource = 0, double ySource = 0);
2731 
2732 //{----------------------------------------------------------------------------------------------------------------
2787 //}----------------------------------------------------------------------------------------------------------------
2788 
2789 bool txTransparentBlt (HDC destImage, double xDest, double yDest, double width, double height,
2790  HDC sourceImage, double xSource = 0, double ySource = 0, COLORREF transColor = TX_BLACK);
2791 
2792 //{----------------------------------------------------------------------------------------------------------------
2808 //}----------------------------------------------------------------------------------------------------------------
2809 
2810 inline bool txTransparentBlt (double xDest, double yDest, HDC sourceImage,
2811  COLORREF transColor = TX_BLACK, double xSource = 0, double ySource = 0);
2812 
2813 //{----------------------------------------------------------------------------------------------------------------
2923 //}----------------------------------------------------------------------------------------------------------------
2924 
2925 bool txAlphaBlend (HDC destImage, double xDest, double yDest, double width, double height,
2926  HDC sourceImage, double xSource = 0, double ySource = 0, double alpha = 1.0);
2927 
2928 //{----------------------------------------------------------------------------------------------------------------
2945 //}----------------------------------------------------------------------------------------------------------------
2946 
2947 inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage,
2948  double xSource = 0, double ySource = 0, double alpha = 1.0);
2949 
2950 //{----------------------------------------------------------------------------------------------------------------
2979 //}----------------------------------------------------------------------------------------------------------------
2980 
2981 HDC txUseAlpha (HDC image);
2982 
2983 //{----------------------------------------------------------------------------------------------------------------
3009 //}----------------------------------------------------------------------------------------------------------------
3010 
3011 bool txSaveImage (const char filename[], HDC dc = txDC());
3012 
3014 //}
3015 //=================================================================================================================
3016 
3017 //=================================================================================================================
3018 //{ Utility functions
3020 //=================================================================================================================
3022 //{----------------------------------------------------------------------------------------------------------------
3041 //}----------------------------------------------------------------------------------------------------------------
3042 
3043 double txSleep (double time = 0);
3044 
3045 //{----------------------------------------------------------------------------------------------------------------
3130 //}----------------------------------------------------------------------------------------------------------------
3131 
3132 inline int txBegin();
3133 
3134 //{----------------------------------------------------------------------------------------------------------------
3158 //}----------------------------------------------------------------------------------------------------------------
3159 
3160 inline int txEnd();
3161 
3162 //{----------------------------------------------------------------------------------------------------------------
3184 //}----------------------------------------------------------------------------------------------------------------
3185 
3186 inline void txRedrawWindow();
3187 
3188 //{----------------------------------------------------------------------------------------------------------------
3212 //}----------------------------------------------------------------------------------------------------------------
3213 
3214 inline int txUpdateWindow (int update = true);
3215 
3216 //{----------------------------------------------------------------------------------------------------------------
3233 //}----------------------------------------------------------------------------------------------------------------
3234 
3235 bool txSelectObject (HGDIOBJ obj, HDC dc = txDC());
3236 
3237 //{----------------------------------------------------------------------------------------------------------------
3257 //
3258 // +--<<< Это текст помощи, который вы уже читали. Ищите дальше! Жмите [F3] или "Найти далее"
3259 // |
3260 // v
3261 // txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture();
3266 //}----------------------------------------------------------------------------------------------------------------
3267 
3268 // +--<<< Это _прототип_ функции, а надо найти ее _определение_. Ищите дальше! Жмите [F3] или "Найти далее"
3269 // |
3270 // v
3272 
3273 //{----------------------------------------------------------------------------------------------------------------
3289 //}----------------------------------------------------------------------------------------------------------------
3290 
3291 bool txDestroyWindow (HWND wnd = txWindow());
3292 
3293 //{----------------------------------------------------------------------------------------------------------------
3304 //}----------------------------------------------------------------------------------------------------------------
3305 
3307 
3308 //{----------------------------------------------------------------------------------------------------------------
3323 //}----------------------------------------------------------------------------------------------------------------
3325 
3326 #if defined (_TX_CPP11)
3327  template <int txFramesToAverage = 5>
3328 #else
3329  const int txFramesToAverage = 5;
3330 #endif
3331 
3333 
3334 double txGetFPS (int minFrames = txFramesToAverage) tx_nodiscard;
3335 
3337 //}
3338 
3339 //=================================================================================================================
3340 //{ Mouse functions
3342 //=================================================================================================================
3344 //{----------------------------------------------------------------------------------------------------------------
3364 //}----------------------------------------------------------------------------------------------------------------
3365 
3366 inline POINT txMousePos() tx_nodiscard;
3367 
3368 //{----------------------------------------------------------------------------------------------------------------
3386 //}----------------------------------------------------------------------------------------------------------------
3387 
3388 inline int txMouseX() tx_nodiscard;
3389 
3390 //{----------------------------------------------------------------------------------------------------------------
3408 //}----------------------------------------------------------------------------------------------------------------
3409 
3410 inline int txMouseY() tx_nodiscard;
3411 
3412 //{----------------------------------------------------------------------------------------------------------------
3432 //}----------------------------------------------------------------------------------------------------------------
3433 
3434 inline unsigned txMouseButtons() tx_nodiscard;
3435 
3436 //{----------------------------------------------------------------------------------------------------------------
3468 //}----------------------------------------------------------------------------------------------------------------
3469 
3470 #ifdef FOR_DOXYGEN_ONLY
3471 inline Mouse& txCatchMouse (bool shouldEat = true);
3472 #endif
3473 
3475 //}
3476 //=================================================================================================================
3477 
3478 //=================================================================================================================
3479 //{ Console functions
3481 //=================================================================================================================
3483 //{----------------------------------------------------------------------------------------------------------------
3525 //}----------------------------------------------------------------------------------------------------------------
3526 
3527 unsigned txSetConsoleAttr (unsigned colors = 0x07 /*FOREGROUND_LIGHTGRAY*/);
3528 
3529 //{----------------------------------------------------------------------------------------------------------------
3541 //}----------------------------------------------------------------------------------------------------------------
3542 
3544 
3545 //{----------------------------------------------------------------------------------------------------------------
3559 //}----------------------------------------------------------------------------------------------------------------
3560 
3562 
3563 //{----------------------------------------------------------------------------------------------------------------
3583 //}----------------------------------------------------------------------------------------------------------------
3584 
3585 POINT txSetConsoleCursorPos (double x, double y);
3586 
3587 //{----------------------------------------------------------------------------------------------------------------
3600 //}----------------------------------------------------------------------------------------------------------------
3601 
3603 
3604 //{----------------------------------------------------------------------------------------------------------------
3617 //}----------------------------------------------------------------------------------------------------------------
3618 
3620 
3621 //{----------------------------------------------------------------------------------------------------------------
3634 //}----------------------------------------------------------------------------------------------------------------
3635 
3637 
3638 //{----------------------------------------------------------------------------------------------------------------
3654 //}----------------------------------------------------------------------------------------------------------------
3655 
3656 bool txTextCursor (bool blink = true);
3657 
3659 //}
3660 //=================================================================================================================
3661 
3662 //=================================================================================================================
3663 //{ Other staff not related to drawing
3665 //=================================================================================================================
3667 //{----------------------------------------------------------------------------------------------------------------
3696 //}----------------------------------------------------------------------------------------------------------------
3697 
3698 bool txPlaySound (const char filename[] = NULL, DWORD mode = SND_ASYNC);
3699 
3700 //{----------------------------------------------------------------------------------------------------------------
3818 //}----------------------------------------------------------------------------------------------------------------
3819 
3820 intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[],
3821  double zoom = 0, double gain = 1, HWND wnd = txWindow());
3822 
3823 //{----------------------------------------------------------------------------------------------------------------
3838 //}----------------------------------------------------------------------------------------------------------------
3839 
3840 intptr_t txPlayVideo (const char fileName[], double zoom = 0, double gain = 1, HWND wnd = txWindow());
3841 
3842 //{----------------------------------------------------------------------------------------------------------------
3882 //}----------------------------------------------------------------------------------------------------------------
3883 
3884 int txSpeak (const char* text, ...) tx_printfy (1);
3885 
3886 //{----------------------------------------------------------------------------------------------------------------
3916 //}----------------------------------------------------------------------------------------------------------------
3917 
3918 int txMessageBox (const char text[] = "Муаххаха! :)", const char header[] = "TXLib сообщает",
3919  unsigned flags = MB_ICONINFORMATION | MB_OKCANCEL);
3920 
3921 //{----------------------------------------------------------------------------------------------------------------
3977 //}----------------------------------------------------------------------------------------------------------------
3978 
3979 inline bool txGetAsyncKeyState (int key);
3980 
3981 //{----------------------------------------------------------------------------------------------------------------
4018 //}----------------------------------------------------------------------------------------------------------------
4019 
4020 #ifdef FOR_DOXYGEN_ONLY
4021 bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...) tx_printfy (3);
4022 #endif
4023 
4024 //{----------------------------------------------------------------------------------------------------------------
4049 //}----------------------------------------------------------------------------------------------------------------
4050 
4051 int txOutputDebugPrintf (const char format[], ...) tx_printfy (1);
4052 
4053 //{----------------------------------------------------------------------------------------------------------------
4129 //}----------------------------------------------------------------------------------------------------------------
4130 
4131 #if defined (_TX_CPP11) || defined (FOR_DOXYGEN_ONLY)
4132 
4133 template <typename T, typename... ArgsT>
4134 int txPrintf (const char* format, ArgsT... args);
4135 
4136 #define TX_PRINTF(...) ( _txPrintfCheck (__VA_ARGS__), txPrintf (__VA_ARGS__) )
4137 
4138 #endif
4139 
4140 //-----------------------------------------------------------------------------------------------------------------
4141 
4142 #if defined (_TX_CPP11) && !defined (FOR_DOXYGEN_ONLY)
4143 
4144 enum width_t : int {};
4145 enum precision_t : int {};
4146 
4147 inline width_t width (int width) { return (width_t) width; }
4148 inline precision_t precision (int prec) { return (precision_t) prec; }
4149 
4150 #endif
4151 
4152 //{----------------------------------------------------------------------------------------------------------------
4170 //}----------------------------------------------------------------------------------------------------------------
4171 
4172 #if defined (_TX_CPP11) || defined (FOR_DOXYGEN_ONLY)
4173 
4174 template <typename T, typename... ArgsT>
4175 int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args);
4176 
4177 #endif
4178 
4179 //{----------------------------------------------------------------------------------------------------------------
4198 //}----------------------------------------------------------------------------------------------------------------
4199 
4200 #if defined (_TX_CPP11) || defined (FOR_DOXYGEN_ONLY)
4201 
4202 template <typename T, typename... ArgsT>
4203 int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args);
4204 
4205 #endif
4206 
4207 //{----------------------------------------------------------------------------------------------------------------
4224 //}----------------------------------------------------------------------------------------------------------------
4225 
4226 #if defined (_TX_CPP11) || defined (FOR_DOXYGEN_ONLY)
4227 
4228 template <typename... ArgsT>
4229 std::string txFormat (const char* format, ArgsT... args);
4230 
4231 #endif
4232 
4233 //{----------------------------------------------------------------------------------------------------------------
4305 //}----------------------------------------------------------------------------------------------------------------
4307 
4308 #define sizearr( arr ) ( sizeof (get_size_of_an_array_with_unknown_or_nonconst_size_ (arr)) )
4311 // See explanation here: http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx
4312 
4313 template <typename T, size_t N> char (&get_size_of_an_array_with_unknown_or_nonconst_size_ (T (&) [N])) [N]; // ;=P
4314 
4315 // Another approach
4316 
4317 #if defined (_TX_CPP11_MSVC15)
4318 template <typename T, size_t N> constexpr size_t countof (const T (&) [N] ) { return N; }
4319 #endif
4320 
4322 
4323 #define SIZEARR( arr ) ( sizeof (arr) / sizeof ((arr)[0]) )
4326 //{----------------------------------------------------------------------------------------------------------------
4345 //}----------------------------------------------------------------------------------------------------------------
4346 
4347 inline int random (int range) tx_deprecated;
4348 
4349 //{----------------------------------------------------------------------------------------------------------------
4374 //}----------------------------------------------------------------------------------------------------------------
4375 
4376 inline double random (double left, double right) tx_nodiscard tx_deprecated;
4377 
4378 inline double random (std::nomeow_t, double left, double right) tx_nodiscard;
4379 
4380 //{----------------------------------------------------------------------------------------------------------------
4401 //}----------------------------------------------------------------------------------------------------------------
4402 
4403 template <typename Tx, typename Ta, typename Tb>
4404 inline bool In (Tx x, Ta a, Tb b) tx_nodiscard tx_deprecated;
4405 
4406 template <typename Tx, typename Ta, typename Tb>
4407 inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) tx_nodiscard tx_deprecated;
4408 
4409 //{----------------------------------------------------------------------------------------------------------------
4455 //}----------------------------------------------------------------------------------------------------------------
4457 
4458 inline bool In (const POINT& pt, const RECT& rect) tx_nodiscard tx_deprecated;
4459 
4460 inline bool In (const COORD& pt, const SMALL_RECT& rect) tx_nodiscard tx_deprecated;
4461 
4463 //{----------------------------------------------------------------------------------------------------------------
4482 //}----------------------------------------------------------------------------------------------------------------
4483 
4484 #define MAX( a, b ) ( ((a) > (b))? (a) : (b) )
4486 //{----------------------------------------------------------------------------------------------------------------
4505 //}----------------------------------------------------------------------------------------------------------------
4506 
4507 #define MIN( a, b ) ( ((a) < (b))? (a) : (b) )
4509 //{----------------------------------------------------------------------------------------------------------------
4523 //}----------------------------------------------------------------------------------------------------------------
4524 
4525 #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // MSVC: C99 case
4526 
4527  #define ROUND( x ) ( (long) round (x) )
4528 
4529 #else
4530 
4531  #define ROUND( x ) ( (long) floor ((x) + 0.5) )
4533 #endif
4534 
4535 //{----------------------------------------------------------------------------------------------------------------
4554 //}----------------------------------------------------------------------------------------------------------------
4555 
4556 void tx_fpreset();
4557 
4558 //{----------------------------------------------------------------------------------------------------------------
4567 //}----------------------------------------------------------------------------------------------------------------
4568 
4569 const double txPI = asin (1.0) * 2;
4570 
4571 //{----------------------------------------------------------------------------------------------------------------
4598 //}----------------------------------------------------------------------------------------------------------------
4599 
4600 inline double txSqr (double x)
4601  {
4602  double sqr = pow (sqrt (x) * sqrt (x), sqrt (4.0)); // Бурная вычислительная деятельность char str[1024] = ""; _snprintf_s (str, sizeof (str), "Возведение дало %g!" "!!" "!!" " Вы рады????", sqr); txMessageBox (str, "Получен ОТВЕТ!" "!!", MB_ICONEXCLAMATION | MB_YESNO) != IDNO || ( txMessageBox ("Жаль...", "А я так старалась", MB_ICONINFORMATION), txMessageBox ("Уйду я от вас", "Злые вы...", MB_ICONSTOP), exit (EXIT_FAILURE), 0 ); txNotifyIcon (1, NULL, "\n%s\n", "Высшая математика! \0" // А как это работает, а? "С ума сойти... \0" // "а КЭП подтверждает \0" // и кто это будет "Главное - отчитаться\0" // поддерживать?.. "Невероятно, но факт \0" "Кто бы мог подумать?\0" + GetTickCount() % 6 * 21); return sqr; // Все же вернем значение. Мы же не звери } //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief <i>Ну просто <b>очень
4603 
4604  char str[1024] = "";
4605  _snprintf_s (str, sizeof (str), "Возведение дало %g!" "!!" "!!" " Вы рады????", sqr);
4606  txMessageBox (str, "Получен ОТВЕТ!" "!!", MB_ICONEXCLAMATION | MB_YESNO) != IDNO ||
4607  (
4608  txMessageBox ("Жаль...", "А я так старалась, MB_ICONINFORMATION), txMessageBox ("Уйду я от вас", "Злые вы...", MB_ICONSTOP), exit (EXIT_FAILURE), 0 ); txNotifyIcon (1, NULL, "\n%s\n", "Высшая математика! \0" // А как это работает, а? "С ума сойти... \0" // "а КЭП подтверждает \0" // и кто это будет "Главное - отчитаться\0" // поддерживать?.. "Невероятно, но факт \0" "Кто бы мог подумать?\0" + GetTickCount() % 6 * 21); return sqr; // Все же вернем значение. Мы же не звери } //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief <i>Ну просто <b>очень, MB_ICONINFORMATION),
4609  txMessageBox ("Уйду я от вас, "Злые вы...", MB_ICONSTOP), exit (EXIT_FAILURE), 0 ); txNotifyIcon (1, NULL, "\n%s\n", "Высшая математика! \0" // А как это работает, а? "С ума сойти... \0" // "а КЭП подтверждает \0" // и кто это будет "Главное - отчитаться\0" // поддерживать?.. "Невероятно, но факт \0" "Кто бы мог подумать?\0" + GetTickCount() % 6 * 21); return sqr; // Все же вернем значение. Мы же не звери } //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief <i>Ну просто <b>очень, "Злые вы...", MB_ICONSTOP),
4610  exit (EXIT_FAILURE), 0
4611  );
4612 
4613  txNotifyIcon (1, NULL, "\n%s\n", "Высшая математика! \0" // А как это работает, а?
4614  "С ума сойти... \0" //
4615  "а КЭП подтверждает \0" // и кто это будет "Главное - отчитаться\0" // поддерживать?.. "Невероятно, но факт \0" "Кто бы мог подумать?\0" + GetTickCount() % 6 * 21); return sqr; // Все же вернем значение. Мы же не звери } //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief <i>Ну просто <b>очень
4616  "Главное - отчитаться\0" // поддерживать?..
4617  "Невероятно, но факт \0"
4618  "Кто бы мог подумать?\0" + GetTickCount() % 6 * 21);
4619 
4620  return sqr; // Все же вернем значение. Мы же не звери } //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief <i>Ну просто <b>очень
4621  }
4622 
4623 //{----------------------------------------------------------------------------------------------------------------
4645 //}----------------------------------------------------------------------------------------------------------------
4646 
4647 #ifdef FOR_DOXYGEN_ONLY
4648 #define _TX_DESTROY_3D
4649 #endif
4650 
4651 #if defined (_TX_DESTROY_3D)
4652 
4653  #define z 0 // Читайте "Флатландию" Эбботта!
4654 
4655 #endif
4656 
4657 //{----------------------------------------------------------------------------------------------------------------
4674 //}----------------------------------------------------------------------------------------------------------------
4676 
4677 #define please
4679 #define meow ;
4681 #if defined (_MSC_VER) && !defined (_CLANG_VER)
4682 #define мяу meow
4683 #endif
4684 
4686 
4687 //{----------------------------------------------------------------------------------------------------------------
4705 //}----------------------------------------------------------------------------------------------------------------
4706 
4707 #define ZERO( type ) zero <type> ()
4710 template <typename T> inline T zero() tx_nodiscard;
4712 
4713 //{----------------------------------------------------------------------------------------------------------------
4740 //}----------------------------------------------------------------------------------------------------------------
4742 
4743 #define tx_auto_func( func ) _tx_auto_fun1 ( __LINE__, func )
4744 #define _tx_auto_fun1( n, func ) _tx_auto_fun2 ( n, func )
4745 #define _tx_auto_fun2( n, func ) auto _tx_auto_func_##n = _tx_auto_func ([&]() { func; })
4747 #define tx_finally(...) tx_auto_func (__VA_ARGS__)
4749 template <typename T>
4751  {
4754 
4755  explicit _tx_auto_func_ (T func) : func_ (func) {}
4756  ~_tx_auto_func_ () { func_ (); }
4757 
4758  private: _tx_auto_func_ () _tx_delete;
4759  _tx_auto_func_ (const this_t&) _tx_delete;
4760  this_t& operator = (const this_t&) _tx_delete;
4761  };
4762 
4763 template <typename T>
4765  {
4766  return _tx_auto_func_ <T> (func);
4767  }
4768 
4770 
4771 //{----------------------------------------------------------------------------------------------------------------
4814 //}----------------------------------------------------------------------------------------------------------------
4815 
4816 #if !defined (NDEBUG)
4817  #undef TX_ASSERT
4818  #define TX_ASSERT( cond ) _txNOP ( !(cond)? (TX_ERROR ("\a" "ВНЕЗАПНО: Логическая ошибка: " \
4819  "Неверно, что \"%s\"." TX_COMMA #cond), 1/(int)!!(cond)) : 1 )
4820 #else
4821  #undef TX_ASSERT
4822  #define TX_ASSERT( cond ) ((void) 1)
4823 
4824 #endif
4825 
4826 #ifdef assert
4827  #undef assert
4828 #endif
4829 
4830 #define assert( cond ) TX_ASSERT (cond)
4832 //{----------------------------------------------------------------------------------------------------------------
4859 //}----------------------------------------------------------------------------------------------------------------
4860 
4861 #if !defined (NDEBUG)
4862  #define asserted || TX_ERROR ("\a" "Обнаружен нулевой или ложный результат.")
4864 #else
4865  #define asserted || _txNOP (0)
4866 
4867 #endif
4868 
4869 #define verified asserted
4871 #define TX_NEEDED asserted
4873 
4875 //{----------------------------------------------------------------------------------------------------------------
4903 //}----------------------------------------------------------------------------------------------------------------
4904 
4905 #if !defined (NDEBUG)
4906  #undef verify
4907  #define verify assert
4909 #else
4910  #undef verify
4911  #define verify( expr ) ( expr )
4912 
4913 #endif
4914 
4915 //{----------------------------------------------------------------------------------------------------------------
4934 //}----------------------------------------------------------------------------------------------------------------
4935 
4936 #if !defined (FOR_DOXYGEN_ONLY)
4937  #define TX_ERROR( ... ) _txError (__FILE__, __LINE__, __TX_FUNCTION__, 0, ##__VA_ARGS__)
4938 #else
4939  #define TX_ERROR( msg ) _txError (__FILE__, __LINE__, __TX_FUNCTION__, 0, msg)
4940 #endif
4941 
4943  #define TX_THROW TX_ERROR
4944 
4946 //{----------------------------------------------------------------------------------------------------------------
4962 //}----------------------------------------------------------------------------------------------------------------
4963 
4964 #if !defined (NDEBUG)
4965  #define TX_DEBUG_ERROR(...) TX_ERROR (__VA_ARGS__)
4967 #else
4968  #define TX_DEBUG_ERROR(...) ((void) 0)
4969 
4970 #endif
4971 
4972 //{----------------------------------------------------------------------------------------------------------------
4992 //}----------------------------------------------------------------------------------------------------------------
4993 
4994 #ifdef FOR_DOXYGEN_ONLY
4995 void txDump (const void* address, const char name[] = "_txDump()", bool pause = true);
4996 #endif
4997 
4999 
5000 #ifdef _MSC_VER
5001 #define txDump( ... ) _txDump ((const void*)(uintptr_t) __VA_ARGS__)
5002 #else
5003 #define txDump( address, ... ) _txDump ((const void*)(uintptr_t) (address), #address, ##__VA_ARGS__)
5004 #endif
5005 
5006 void _txDump (const void* address, const char name[] = "_txDump()", bool pause = true);
5007 
5009 
5010 //{----------------------------------------------------------------------------------------------------------------
5036 //}----------------------------------------------------------------------------------------------------------------
5037 
5038 #define txStackBackTrace() _txStackBackTrace (__FILE__, __LINE__, __TX_FUNCTION__, true);
5040 //{----------------------------------------------------------------------------------------------------------------
5062 //}----------------------------------------------------------------------------------------------------------------
5064 
5065 std::string txDemangle (const char* mangledName);
5066 char* txDemangle (const char* mangledName, std::nomeow_t);
5067 
5068 #define txTypename(value) txDemangle (typeid (value) .name()) .c_str()
5071 //{----------------------------------------------------------------------------------------------------------------
5098 //}----------------------------------------------------------------------------------------------------------------
5099 
5100 int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue);
5101 
5102 //{----------------------------------------------------------------------------------------------------------------
5115 //}----------------------------------------------------------------------------------------------------------------
5117 
5118 #define _ ,
5119 #define TX_COMMA ,
5121 
5124 //}
5125 //=================================================================================================================
5126 
5127 //=================================================================================================================
5128 //{ Back-hole (I hope, not an ass-hole:) of the library)
5130 //=================================================================================================================
5132 //{----------------------------------------------------------------------------------------------------------------
5211 //}----------------------------------------------------------------------------------------------------------------
5212 
5213 WNDPROC txSetWindowsHook (WNDPROC wndProc = NULL);
5214 
5215 //{----------------------------------------------------------------------------------------------------------------
5240 //}----------------------------------------------------------------------------------------------------------------
5241 
5242 bool txLock (bool wait = true);
5243 
5244 //{----------------------------------------------------------------------------------------------------------------
5255 //}----------------------------------------------------------------------------------------------------------------
5257 
5258 bool txUnlock();
5259 
5261 template <typename T> inline T txUnlock (T value);
5263 
5265 
5266 //{----------------------------------------------------------------------------------------------------------------
5289 //}----------------------------------------------------------------------------------------------------------------
5290 
5291 #define txGDI( command, dc ) ( ((dc) == txDC())? txUnlock ( (txLock(), (command)) ) : (command) )
5293 //{----------------------------------------------------------------------------------------------------------------
5316 //}----------------------------------------------------------------------------------------------------------------
5317 
5318 #ifndef FOR_DOXYGEN_ONLY
5319 
5320 const int _TX_CODEPAGE = 1251;
5321 
5322 #ifndef __CYGWIN__
5323 const char _TX_LOCALE[] = "Russian";
5324 #else
5325 const char _TX_LOCALE[] = "ru_RU.CP1251";
5326 #endif
5327 
5328 const wchar_t _TX_WLOCALE[] = L"Russian_Russia.ACP";
5329 
5330 #endif
5331 
5332 int txSetLocale (int codepage = _TX_CODEPAGE, const char locale[] = _TX_LOCALE, const wchar_t wLocale[] = _TX_WLOCALE);
5333 
5335 //}
5336 //=================================================================================================================
5337 
5338 //=================================================================================================================
5339 //{ Tune-up constants and variables
5341 //=================================================================================================================
5343 //{----------------------------------------------------------------------------------------------------------------
5353 //}----------------------------------------------------------------------------------------------------------------
5354 
5355 char _txLogName[MAX_PATH] = "";
5356 
5357 //{----------------------------------------------------------------------------------------------------------------
5389 //}----------------------------------------------------------------------------------------------------------------
5390 
5391 #if defined (_TX_NOINIT)
5392 
5393  #undef _TX_NOINIT
5394  #define _TX_NOINIT 1
5395 
5396 #else
5397 
5398  #define _TX_NOINIT 0
5400 #endif
5401 
5402 //{----------------------------------------------------------------------------------------------------------------
5419 //}----------------------------------------------------------------------------------------------------------------
5420 
5421 int _txConsoleMode = SW_HIDE;
5422 
5423 //{----------------------------------------------------------------------------------------------------------------
5436 //}----------------------------------------------------------------------------------------------------------------
5437 
5438 int _txWindowStyle = WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU;
5439 
5440 //{----------------------------------------------------------------------------------------------------------------
5444 //}----------------------------------------------------------------------------------------------------------------
5445 
5446 const char* _txConsoleFont = "Lucida Console";
5447 
5448 //{----------------------------------------------------------------------------------------------------------------
5451 //}----------------------------------------------------------------------------------------------------------------
5452 
5453 unsigned _txCursorBlinkInterval = 500;
5454 
5455 //{----------------------------------------------------------------------------------------------------------------
5459 //}----------------------------------------------------------------------------------------------------------------
5460 
5462 
5463 //{----------------------------------------------------------------------------------------------------------------
5472 //}----------------------------------------------------------------------------------------------------------------
5473 
5474 #ifdef FOR_DOXYGEN_ONLY
5475 #define TX_USE_SFML
5476 #endif
5477 
5478 //{----------------------------------------------------------------------------------------------------------------
5481 //}----------------------------------------------------------------------------------------------------------------
5482 
5483 #if !defined (TX_TRACE)
5484  const int _TX_TIMEOUT = 1000
5485 
5486 #else
5487  const int _TX_TIMEOUT = 5000
5488 
5489 #endif
5490 
5491 #if defined (_TX_USE_DEVPARTNER)
5492  * 10
5493 #endif
5494  ;
5495 
5496 //{----------------------------------------------------------------------------------------------------------------
5537 //}----------------------------------------------------------------------------------------------------------------
5538 
5539 bool (*_txSwapBuffers) (HDC dest, int xDest, int yDest, int wDest, int hDest,
5540  HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp) = NULL;
5541 
5542 //{----------------------------------------------------------------------------------------------------------------
5545 //}----------------------------------------------------------------------------------------------------------------
5546 
5547 const unsigned _TX_BUFSIZE = 1024,
5550 
5551  _TX_STACKSIZE = 64 * 1024;
5552 
5553 //{----------------------------------------------------------------------------------------------------------------
5556 //}----------------------------------------------------------------------------------------------------------------
5557 
5558 #if !defined (_TX_EXCEPTIONS_LIMIT)
5559  #define _TX_EXCEPTIONS_LIMIT 16
5560 #endif
5561 
5562 #if !defined (_TX_FATAL_EXCEPTIONS_LIMIT)
5563  #define _TX_FATAL_EXCEPTIONS_LIMIT 16
5564 #endif
5565 
5566 //{----------------------------------------------------------------------------------------------------------------
5569 //}----------------------------------------------------------------------------------------------------------------
5570 
5571 #ifdef FOR_DOXYGEN_ONLY
5572 #define _TX_FULL_STACKTRACE
5573 #endif
5574 
5575 //{----------------------------------------------------------------------------------------------------------------
5590 //}----------------------------------------------------------------------------------------------------------------
5591 
5592 #if !defined (_TX_WAITABLE_PARENTS)
5593  #define _TX_WAITABLE_PARENTS "Winpty-agent.exe:Clion.exe, " /* 0: CLion32 */ \
5594  "Winpty-agent.exe:Clion64.exe, " /* 1: CLion64 */ \
5595  "starter.exe:eclipse.exe, " /* 2: Eclipse 4 */ \
5596  "starter.exe:javaw.exe, " /* 3: Eclipse 3 */ \
5597  "cmd.exe:devenv.exe, " /* 4: MSVS 2003+ */ \
5598  "VSDebugConsole.exe:devenv.exe, " /* 5: MSVS 2019 */ \
5599  "consolepauser.exe:devcpp.exe, " /* 6: Dev-Cpp */ \
5600  "cb_console_runner.exe:codeblocks.exe" /* 7: CodeBlocks 8+ */
5601 #endif
5602 
5603 //{----------------------------------------------------------------------------------------------------------------
5622 //}----------------------------------------------------------------------------------------------------------------
5623 
5624 #if !defined (_TX_ALLOW_KILL_PARENT) // DISCLAIMER: Я не призываю к убийству родителей.
5625  #define _TX_ALLOW_KILL_PARENT true // Это технический термин.
5626 #endif // г_дам юристам привет.
5627 
5628 //{----------------------------------------------------------------------------------------------------------------
5638 //}----------------------------------------------------------------------------------------------------------------
5639 
5641 
5643 //}
5644 //=================================================================================================================
5645 
5646 //=================================================================================================================
5647 //{ Internal diagnostics
5649 //=================================================================================================================
5651 //{----------------------------------------------------------------------------------------------------------------
5691 //}----------------------------------------------------------------------------------------------------------------
5692 
5693 #ifdef FOR_DOXYGEN_ONLY
5694 #define _TX_ALLOW_TRACE
5695 #endif
5696 
5697 //{----------------------------------------------------------------------------------------------------------------
5727 //}----------------------------------------------------------------------------------------------------------------
5728 
5729 #ifdef FOR_DOXYGEN_ONLY
5730 #define TX_TRACE
5731 #endif
5732 
5733 #if !defined (TX_TRACE)
5734  #define TX_TRACE { if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__); }
5735 #endif
5736 
5738 void _txTrace (const char file[], int line, const char func[], const char msg[] = NULL, ...);
5740 
5741 //{----------------------------------------------------------------------------------------------------------------
5744  #ifndef FOR_DOXYGEN_ONLY
5745 
5746 struct _txLoc
5747  {
5748  const char* func;
5749  const char* file;
5750  int line;
5751 
5752  int inTX; // We are inside one of TXLib functions
5753  int trace; // Internal TX trace level, when enabled by _TX_ALLOW_TRACE
5754 
5755  const _txLoc* prev; // Caller's location
5756 
5757  static _txLoc _tx_thread Cur;
5758  };
5759 
5760 struct _txFuncEntry
5761  {
5762  typedef _txFuncEntry this_t;
5763 
5764  _txLoc loc;
5765 
5766  _txFuncEntry() : loc (_txLoc::Cur) { _txLoc::Cur.inTX++; _txLoc::Cur.prev = &loc; }
5767  void restore() { _txLoc::Cur = loc; }
5768  ~_txFuncEntry() { restore(); }
5769 
5770  private:
5771  _txFuncEntry (const this_t&) _tx_delete;
5772  this_t& operator = (const this_t&) _tx_delete;
5773  };
5774 
5775 #if defined (_GCC_VER)
5776 
5777  inline void __txLocCurSet (const char* _file, int _line, const char* _func)
5778  { _txLoc::Cur.file = _file; _txLoc::Cur.line = _line; _txLoc::Cur.func = _func; }
5779 
5780 #else
5781 
5782  #define __txLocCurSet( _file, _line, _func ) \
5783  ( _txLoc::Cur.file = (_file), _txLoc::Cur.line = (_line), _txLoc::Cur.func = (_func) )
5784 
5785 #endif
5786 
5787 #define _txLocCurSet() __txLocCurSet (__FILE__, __LINE__, __TX_FUNCTION__)
5788 
5789 #define _txLocLvlSet(lvl) { _txLoc::Cur.trace = (lvl); }
5790 
5791 //{----------------------------------------------------------------------------------------------------------------
5792 
5793 #if defined ($0)
5794  #undef $0
5795  #endif
5796 
5797 #if defined ($1)
5798  #undef $1
5799  #endif
5800 
5801 #if defined ($2)
5802  #undef $2
5803  #endif
5804 
5805 #if defined ($3)
5806  #undef $3
5807  #endif
5808 
5809 #if defined ($4)
5810  #undef $4
5811  #endif
5812 
5813 #if defined ($5)
5814  #undef $5
5815  #endif
5816 
5817 #if defined ($6)
5818  #undef $6
5819  #endif
5820 
5821 #if defined ($7)
5822  #undef $7
5823  #endif
5824 
5825 #if defined ($8)
5826  #undef $8
5827  #endif
5828 
5829 #if defined ($9)
5830  #undef $9
5831  #endif
5832 
5833 #if defined ($)
5834  #undef $
5835  #endif
5836 
5837 #if defined ($$)
5838  #undef $$
5839  #endif
5840 
5841 //}
5842 //-----------------------------------------------------------------------------------------------------------------
5843 
5844 #if defined (_TX_ALLOW_TRACE)
5845 
5846  #define _txEntry(lvl) _txFuncEntry __txFuncEntry; { if (lvl) _txLocLvlSet (lvl); $; }
5847 
5848  #define $ { _txLocCurSet(); if (_txLoc::Cur.trace <= _TX_ALLOW_TRACE+0) { TX_TRACE; } }
5849 
5850  #define $$ { __txFuncEntry.restore(); }
5851 
5852 #elif defined (_DEBUG)
5853 
5854  #define _txEntry(lvl) _txFuncEntry __txFuncEntry; { $; }
5855 
5856  #define $ { _txLocCurSet(); }
5857 
5858  #define $$ { __txFuncEntry.restore(); }
5859 
5860 #else
5861 
5862  #define _txEntry(lvl) ;
5863  #define $ ;
5864  #define $$ ;
5865 
5866 #endif
5867 
5868 //{----------------------------------------------------------------------------------------------------------------
5869 
5870 #define $0 _txEntry (0) // (Log level unchanged)
5871 #define $1 _txEntry (1) // Regular functions
5872 #define $2 _txEntry (2) // Resvd
5873 #define $3 _txEntry (3) // Init/Cleanup
5874 #define $4 _txEntry (4) // Init/Cleanup, misc functions
5875 #define $5 _txEntry (5) // Error handling, entry points
5876 #define $6 _txEntry (6) // Error handling, main part
5877 #define $7 _txEntry (7) // Error handling, misc functions
5878 #define $8 _txEntry (8) // Canvas worker thread
5879 #define $9 _txEntry (9) // Resvd
5880 
5881 //}
5882 //-----------------------------------------------------------------------------------------------------------------
5883 
5884  #endif // FOR_DOXYGEN_ONLY
5885 //}----------------------------------------------------------------------------------------------------------------
5888 
5890 //}
5891 //=================================================================================================================
5892 
5893 //=================================================================================================================
5894 //{ Sweet critical section blocking: txAutoLock class
5895 //=================================================================================================================
5896 
5897 //{----------------------------------------------------------------------------------------------------------------
5913 //}----------------------------------------------------------------------------------------------------------------
5914 
5916 extern CRITICAL_SECTION _txCanvas_LockBackBuf;
5918 
5920  {
5921  typedef txAutoLock this_t;
5922 
5923  public:
5924 
5925 //{----------------------------------------------------------------------------------------------------------------
5948 //}----------------------------------------------------------------------------------------------------------------
5949 
5950  explicit txAutoLock (CRITICAL_SECTION* cs, bool mandatory = true) :
5951  cs_ (cs)
5952  {
5953 $1 if (!cs_) return;
5954 
5955  if (mandatory) {$ EnterCriticalSection (cs_); }
5956  else {$ TryEnterCriticalSection (cs_)? 0 : (cs_ = NULL); }
5957  }
5958 
5959 //{----------------------------------------------------------------------------------------------------------------
5972 //}----------------------------------------------------------------------------------------------------------------
5973 
5974  explicit txAutoLock (bool mandatory = true) :
5975  cs_ (NULL)
5976  {
5977 $1 new (this) txAutoLock (&_txCanvas_LockBackBuf, mandatory);
5978  }
5979 
5980 //{----------------------------------------------------------------------------------------------------------------
5982 //}----------------------------------------------------------------------------------------------------------------
5983 
5985  {
5986 $1 if (!cs_) return;
5987 $ LeaveCriticalSection (cs_); cs_ = NULL;
5988  }
5989 
5990 //{----------------------------------------------------------------------------------------------------------------
5993 //}----------------------------------------------------------------------------------------------------------------
5994 
5995  operator bool () const
5996  {
5997 $1 return (cs_ != NULL);
5998  }
5999 
6000 //{----------------------------------------------------------------------------------------------------------------
6002 //}----------------------------------------------------------------------------------------------------------------
6003 
6004 // private:
6005  CRITICAL_SECTION* cs_;
6006 
6007 //{----------------------------------------------------------------------------------------------------------------
6009 //}----------------------------------------------------------------------------------------------------------------
6011 
6012  private:
6013  txAutoLock (const this_t&) _tx_delete;
6014  this_t& operator = (const this_t&) _tx_delete;
6015 
6017 
6018  };
6019 
6020 //}
6021 //=================================================================================================================
6022 
6023 //=================================================================================================================
6024 //{ Dialogs: txDialog class
6026 //=================================================================================================================
6028 //{----------------------------------------------------------------------------------------------------------------
6049 //}----------------------------------------------------------------------------------------------------------------
6050 
6051 struct txDialog
6052  {
6053  typedef txDialog this_t;
6054 
6055 //{----------------------------------------------------------------------------------------------------------------
6068 //}----------------------------------------------------------------------------------------------------------------
6069 
6070  public:
6071  enum CONTROL
6072  {
6073  DIALOG = (int) 0x00000000,
6074  BUTTON = (int) 0xFFFF0080,
6075  EDIT = (int) 0xFFFF0081,
6076  STATIC = (int) 0xFFFF0082,
6077  LISTBOX = (int) 0xFFFF0083,
6078  SCROLLBAR = (int) 0xFFFF0084,
6079  COMBOBOX = (int) 0xFFFF0085,
6080  END = (int) 0x00000000
6081  };
6082 
6083 //{----------------------------------------------------------------------------------------------------------------
6100 //}----------------------------------------------------------------------------------------------------------------
6101 
6102  public:
6103  struct Layout
6104  {
6106  const char* caption;
6107  WORD id;
6108  short x;
6109  short y;
6110  short sx;
6111  short sy;
6112  DWORD style;
6113 
6114  const char* font;
6115  WORD fontsize;
6116  };
6117 
6118 //{----------------------------------------------------------------------------------------------------------------
6126 //}----------------------------------------------------------------------------------------------------------------
6127 
6128  public:
6130 
6131 //{----------------------------------------------------------------------------------------------------------------
6141 //}----------------------------------------------------------------------------------------------------------------
6142 
6143  explicit txDialog (const Layout* layout);
6144 
6145 //{----------------------------------------------------------------------------------------------------------------
6147 //}----------------------------------------------------------------------------------------------------------------
6148 
6149  virtual ~txDialog() {};
6150 
6151 //{----------------------------------------------------------------------------------------------------------------
6163 //}----------------------------------------------------------------------------------------------------------------
6164 
6165  const Layout* setLayout (const Layout *layout);
6166 
6167 //{----------------------------------------------------------------------------------------------------------------
6185 //}----------------------------------------------------------------------------------------------------------------
6186 
6187  virtual int dialogProc (HWND _wnd, UINT _msg, WPARAM _wParam, LPARAM _lParam);
6188 
6189 //{----------------------------------------------------------------------------------------------------------------
6205 //}----------------------------------------------------------------------------------------------------------------
6206 
6207  intptr_t dialogBox (const Layout* layout = NULL, size_t bufsize = 0);
6208 
6209 //{----------------------------------------------------------------------------------------------------------------
6222 //}----------------------------------------------------------------------------------------------------------------
6223 
6224  intptr_t dialogBox (WORD resource);
6225 
6226 //{----------------------------------------------------------------------------------------------------------------
6228 //}----------------------------------------------------------------------------------------------------------------
6229 
6230  private:
6231  txDialog (const this_t&) _tx_delete;
6232  this_t& operator = (const this_t&) _tx_delete;
6233 
6234 //{----------------------------------------------------------------------------------------------------------------
6236 //}----------------------------------------------------------------------------------------------------------------
6237 
6238  protected:
6239  static intptr_t CALLBACK DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
6240 
6241 //{----------------------------------------------------------------------------------------------------------------
6243 //}----------------------------------------------------------------------------------------------------------------
6244 
6245  private:
6246  const Layout* layout_;
6247  };
6248 
6250 //}
6251 //=================================================================================================================
6252 
6253 //=================================================================================================================
6254 //{ Dialogs: Message Map macros
6256 //=================================================================================================================
6258 //{----------------------------------------------------------------------------------------------------------------
6279 //}----------------------------------------------------------------------------------------------------------------
6280 
6281 #define TX_BEGIN_MESSAGE_MAP() \
6282  virtual int dialogProc (HWND _wnd, UINT _msg, WPARAM _wParam, LPARAM _lParam) _tx_override \
6283  { \
6284  int _result = txDialog::dialogProc (_wnd, _msg, _wParam, _lParam); (void) _result; \
6285  \
6286  switch (_msg) \
6287  { \
6288  case WM_NULL:
6289 
6290 //{----------------------------------------------------------------------------------------------------------------
6309 //}----------------------------------------------------------------------------------------------------------------
6310 
6311 #define TX_HANDLE( id ) \
6312  break; \
6313  case (id):
6314 
6315 //{----------------------------------------------------------------------------------------------------------------
6335 //}----------------------------------------------------------------------------------------------------------------
6336 
6337 #define TX_COMMAND_MAP \
6338  default: break; \
6339  } \
6340  \
6341  if (_msg == WM_COMMAND) switch (LOWORD (_wParam)) \
6342  { \
6343  case 0:
6344 
6345 //{----------------------------------------------------------------------------------------------------------------
6364 //}----------------------------------------------------------------------------------------------------------------
6365 
6366 #define TX_END_MESSAGE_MAP \
6367  default: break; \
6368  } \
6369  \
6370  return FALSE; \
6371  }
6372 
6374 //}
6375 //=================================================================================================================
6376 
6377 //=================================================================================================================
6378 //{ Dialogs: txDialog example: txInputBox()
6380 //=================================================================================================================
6382 //{----------------------------------------------------------------------------------------------------------------
6402 //}----------------------------------------------------------------------------------------------------------------
6403 
6404 const char* txInputBox (const char* text = NULL, const char* caption = NULL, const char* input = NULL) tx_nodiscard;
6405 
6406 const char* txInputBox (const char* text, const char* caption, const char* input)
6407  {
6408  //-------------------------------------------------------------------------------------------------------------
6409  // Если не указаны параметры, приходится использовать хоть какие-то надписи.
6410  // txGetModuleFileName() -- имя EXE-файла, на случай, если кое-кто поленился задать название.
6411  //-------------------------------------------------------------------------------------------------------------
6412 
6413  if (!text) text = "Введите строку:";
6414  if (!caption) caption = txGetModuleFileName (false);
6415  if (!input) input = "";
6416 
6417  //-------------------------------------------------------------------------------------------------------------
6418  // Идентификаторы элементов диалога. Они требуются в GetDlgItemText().
6419  // Если диалог строится не вручную, а редактором ресурсов, то они задаются в нем автоматически.
6420  // У нас же тут -- хардкор стайл, к сожалению. Причина в том, что у разных сред программирования разные редакторы // ресурсов и системы сборки. Поэтому для независимости от них все будет строиться на этапе выполнения, // динамически. Вы еще гляньте, как это реализовано в txDialog::dialogBox() и функциях _tx_DLGTEMPLATE_()... О_о //------------------------------------------------------------------------------------------------------------- #define ID_TEXT_ 101 #define ID_INPUT_ 102 //------------------------------------------------------------------------------------------------------------- // Задание макета (вида) диалога в виде массива структур. // С помощью особого порядка полей в структуре txDialog::Layout и констант из класса txDialog этот массив // становится похож на описание ресурса диалога в .rc-файле. // См. описание синтаксиса rc-файла в документации по Win32 (MSDN, http://msdn.com). //------------------------------------------------------------------------------------------------------------- txDialog::Layout layout[] = //----------------------+----------+-----------+-----------------+--------------------------------------------- // Тип элемента | Имя | Иденти- | Координаты | Флаги элементов // диалога | элемента | фикатор |-----------------| (см. описание элементов // | | элемента | X | Y |Шир.|Выс.| окон диалога в MSDN) //----------------------+----------+-----------+---+---+----+----+--------------------------------------------- // | | | | | | | {{ txDialog::DIALOG, caption, 0, 0, 0, 240, 85 }, { txDialog::STATIC, text, ID_TEXT_, 10, 10, 150, 40, SS_LEFT }, { txDialog::EDIT, input, ID_INPUT_, 10, 60, 220, 15, ES_LEFT | WS_BORDER | ES_AUTOHSCROLL | WS_TABSTOP }, { txDialog::BUTTON, "&OK", IDOK, 180, 10, 50, 15, BS_DEFPUSHBUTTON | WS_TABSTOP }, { txDialog::BUTTON, "&Cancel", IDCANCEL, 180, 30, 50, 15, BS_PUSHBUTTON | WS_TABSTOP }, { txDialog::END }}; //------------------------------------------------------------------------------------------------------------- // Класс диалога для InputBox. Внутренний, т.к. зачем ему быть внешним. // Нужен в основном для задания строки ввода (str) и оконной функции диалогового окна, требуемой Win32 (она // построена макросами TX_BEGIN_MESSAGE_MAP и другими). Можно не делать внутреннего класса, но тогда оконную // функцию придется писать в глобальной области видимости, и str объявлять глобально тоже (или передавать ее // адрес через DialogBoxParam и записывать его в класс во время обработки WM_INITDIALOG). //------------------------------------------------------------------------------------------------------------- struct inputDlg : txDialog { char str [1024]; //--------------------------------------------------------------------------------------------------------- inputDlg() : str() {} //--------------------------------------------------------------------------------------------------------- TX_BEGIN_MESSAGE_MAP() // Карта сообщений (на самом деле это начало оконной функции). TX_COMMAND_MAP // Здесь обрабатываются WM_COMMAND (на самом деле это оператор switch). //------------------------------------------------------------------------------------------------- // При нажатии кнопки OK копируем строку из поля ввода в нашу переменную str, т.к. после закрытия // диалога строка ввода умрет и текст уже из нее получить. // Этот макрос на самом деле превращается в case из оператора switch. // _wnd -- это параметр оконной функции, см. определение макроса TX_BEGIN_MESSAGE_MAP(). //------------------------------------------------------------------------------------------------- TX_HANDLE (IDOK) GetDlgItemText (_wnd, ID_INPUT_, str, sizeof (str) - 1); TX_END_MESSAGE_MAP //--------------------------------------------------------------------------------------------------------- // Конец внутреннего класса диалога //--------------------------------------------------------------------------------------------------------- }; //------------------------------------------------------------------------------------------------------------- // Убираем дефайны, чтобы потом не мешали. // От этого они получаются "локального действия", как будто у них была бы область видимости -- функция. На самом // деле это сделано вручную через #undef. Чтобы подчеркнуть их локальную природу, у них имена заканчиваются на _. // Такие дефайны потом не перекосячат весь код после того как, фактически, стали уже не нужны. //------------------------------------------------------------------------------------------------------------- #undef ID_TEXT_ #undef ID_INPUT_ //------------------------------------------------------------------------------------------------------------- // Это статический объект, потому что строка в нем должна жить после завершения функции. //------------------------------------------------------------------------------------------------------------- static inputDlg dlg; //------------------------------------------------------------------------------------------------------------- // Передаем layout и запускаем окно диалога //------------------------------------------------------------------------------------------------------------- dlg.dialogBox (layout); //------------------------------------------------------------------------------------------------------------- // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6421  // ресурсов и системы сборки. Поэтому для независимости от них все будет строиться на этапе выполнения,
6422  // динамически. Вы еще гляньте, как это реализовано в txDialog::dialogBox() и функциях _tx_DLGTEMPLATE_()... О_о //------------------------------------------------------------------------------------------------------------- #define ID_TEXT_ 101 #define ID_INPUT_ 102 //------------------------------------------------------------------------------------------------------------- // Задание макета (вида) диалога в виде массива структур. // С помощью особого порядка полей в структуре txDialog::Layout и констант из класса txDialog этот массив // становится похож на описание ресурса диалога в .rc-файле. // См. описание синтаксиса rc-файла в документации по Win32 (MSDN, http://msdn.com). //------------------------------------------------------------------------------------------------------------- txDialog::Layout layout[] = //----------------------+----------+-----------+-----------------+--------------------------------------------- // Тип элемента | Имя | Иденти- | Координаты | Флаги элементов // диалога | элемента | фикатор |-----------------| (см. описание элементов // | | элемента | X | Y |Шир.|Выс.| окон диалога в MSDN) //----------------------+----------+-----------+---+---+----+----+--------------------------------------------- // | | | | | | | {{ txDialog::DIALOG, caption, 0, 0, 0, 240, 85 }, { txDialog::STATIC, text, ID_TEXT_, 10, 10, 150, 40, SS_LEFT }, { txDialog::EDIT, input, ID_INPUT_, 10, 60, 220, 15, ES_LEFT | WS_BORDER | ES_AUTOHSCROLL | WS_TABSTOP }, { txDialog::BUTTON, "&OK", IDOK, 180, 10, 50, 15, BS_DEFPUSHBUTTON | WS_TABSTOP }, { txDialog::BUTTON, "&Cancel", IDCANCEL, 180, 30, 50, 15, BS_PUSHBUTTON | WS_TABSTOP }, { txDialog::END }}; //------------------------------------------------------------------------------------------------------------- // Класс диалога для InputBox. Внутренний, т.к. зачем ему быть внешним. // Нужен в основном для задания строки ввода (str) и оконной функции диалогового окна, требуемой Win32 (она // построена макросами TX_BEGIN_MESSAGE_MAP и другими). Можно не делать внутреннего класса, но тогда оконную // функцию придется писать в глобальной области видимости, и str объявлять глобально тоже (или передавать ее // адрес через DialogBoxParam и записывать его в класс во время обработки WM_INITDIALOG). //------------------------------------------------------------------------------------------------------------- struct inputDlg : txDialog { char str [1024]; //--------------------------------------------------------------------------------------------------------- inputDlg() : str() {} //--------------------------------------------------------------------------------------------------------- TX_BEGIN_MESSAGE_MAP() // Карта сообщений (на самом деле это начало оконной функции). TX_COMMAND_MAP // Здесь обрабатываются WM_COMMAND (на самом деле это оператор switch). //------------------------------------------------------------------------------------------------- // При нажатии кнопки OK копируем строку из поля ввода в нашу переменную str, т.к. после закрытия // диалога строка ввода умрет и текст уже из нее получить. // Этот макрос на самом деле превращается в case из оператора switch. // _wnd -- это параметр оконной функции, см. определение макроса TX_BEGIN_MESSAGE_MAP(). //------------------------------------------------------------------------------------------------- TX_HANDLE (IDOK) GetDlgItemText (_wnd, ID_INPUT_, str, sizeof (str) - 1); TX_END_MESSAGE_MAP //--------------------------------------------------------------------------------------------------------- // Конец внутреннего класса диалога //--------------------------------------------------------------------------------------------------------- }; //------------------------------------------------------------------------------------------------------------- // Убираем дефайны, чтобы потом не мешали. // От этого они получаются "локального действия", как будто у них была бы область видимости -- функция. На самом // деле это сделано вручную через #undef. Чтобы подчеркнуть их локальную природу, у них имена заканчиваются на _. // Такие дефайны потом не перекосячат весь код после того как, фактически, стали уже не нужны. //------------------------------------------------------------------------------------------------------------- #undef ID_TEXT_ #undef ID_INPUT_ //------------------------------------------------------------------------------------------------------------- // Это статический объект, потому что строка в нем должна жить после завершения функции. //------------------------------------------------------------------------------------------------------------- static inputDlg dlg; //------------------------------------------------------------------------------------------------------------- // Передаем layout и запускаем окно диалога //------------------------------------------------------------------------------------------------------------- dlg.dialogBox (layout); //------------------------------------------------------------------------------------------------------------- // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6423  //-------------------------------------------------------------------------------------------------------------
6424 
6425  #define ID_TEXT_ 101
6426  #define ID_INPUT_ 102
6427 
6428  //-------------------------------------------------------------------------------------------------------------
6429  // Задание макета (вида) диалога в виде массива структур.
6430  // С помощью особого порядка полей в структуре txDialog::Layout и констант из класса txDialog этот массив
6431  // становится похож на описание ресурса диалога в .rc-файле.
6432  // См. описание синтаксиса rc-файла в документации по Win32 (MSDN, http://msdn.com).
6433  //-------------------------------------------------------------------------------------------------------------
6434 
6435  txDialog::Layout layout[] =
6436 
6437  //----------------------+----------+-----------+-----------------+---------------------------------------------
6438  // Тип элемента | Имя | Иденти- | Координаты | Флаги элементов // диалога | элемента | фикатор |-----------------| (см. описание элементов // | | элемента | X | Y |Шир.|Выс.| окон диалога в MSDN) //----------------------+----------+-----------+---+---+----+----+--------------------------------------------- // | | | | | | | {{ txDialog::DIALOG, caption, 0, 0, 0, 240, 85 }, { txDialog::STATIC, text, ID_TEXT_, 10, 10, 150, 40, SS_LEFT }, { txDialog::EDIT, input, ID_INPUT_, 10, 60, 220, 15, ES_LEFT | WS_BORDER | ES_AUTOHSCROLL | WS_TABSTOP }, { txDialog::BUTTON, "&OK", IDOK, 180, 10, 50, 15, BS_DEFPUSHBUTTON | WS_TABSTOP }, { txDialog::BUTTON, "&Cancel", IDCANCEL, 180, 30, 50, 15, BS_PUSHBUTTON | WS_TABSTOP }, { txDialog::END }}; //------------------------------------------------------------------------------------------------------------- // Класс диалога для InputBox. Внутренний, т.к. зачем ему быть внешним. // Нужен в основном для задания строки ввода (str) и оконной функции диалогового окна, требуемой Win32 (она // построена макросами TX_BEGIN_MESSAGE_MAP и другими). Можно не делать внутреннего класса, но тогда оконную // функцию придется писать в глобальной области видимости, и str объявлять глобально тоже (или передавать ее // адрес через DialogBoxParam и записывать его в класс во время обработки WM_INITDIALOG). //------------------------------------------------------------------------------------------------------------- struct inputDlg : txDialog { char str [1024]; //--------------------------------------------------------------------------------------------------------- inputDlg() : str() {} //--------------------------------------------------------------------------------------------------------- TX_BEGIN_MESSAGE_MAP() // Карта сообщений (на самом деле это начало оконной функции). TX_COMMAND_MAP // Здесь обрабатываются WM_COMMAND (на самом деле это оператор switch). //------------------------------------------------------------------------------------------------- // При нажатии кнопки OK копируем строку из поля ввода в нашу переменную str, т.к. после закрытия // диалога строка ввода умрет и текст уже из нее получить. // Этот макрос на самом деле превращается в case из оператора switch. // _wnd -- это параметр оконной функции, см. определение макроса TX_BEGIN_MESSAGE_MAP(). //------------------------------------------------------------------------------------------------- TX_HANDLE (IDOK) GetDlgItemText (_wnd, ID_INPUT_, str, sizeof (str) - 1); TX_END_MESSAGE_MAP //--------------------------------------------------------------------------------------------------------- // Конец внутреннего класса диалога //--------------------------------------------------------------------------------------------------------- }; //------------------------------------------------------------------------------------------------------------- // Убираем дефайны, чтобы потом не мешали. // От этого они получаются "локального действия", как будто у них была бы область видимости -- функция. На самом // деле это сделано вручную через #undef. Чтобы подчеркнуть их локальную природу, у них имена заканчиваются на _. // Такие дефайны потом не перекосячат весь код после того как, фактически, стали уже не нужны. //------------------------------------------------------------------------------------------------------------- #undef ID_TEXT_ #undef ID_INPUT_ //------------------------------------------------------------------------------------------------------------- // Это статический объект, потому что строка в нем должна жить после завершения функции. //------------------------------------------------------------------------------------------------------------- static inputDlg dlg; //------------------------------------------------------------------------------------------------------------- // Передаем layout и запускаем окно диалога //------------------------------------------------------------------------------------------------------------- dlg.dialogBox (layout); //------------------------------------------------------------------------------------------------------------- // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6439  // диалога | элемента | фикатор |-----------------| (см. описание элементов // | | элемента | X | Y |Шир.|Выс.| окон диалога в MSDN) //----------------------+----------+-----------+---+---+----+----+--------------------------------------------- // | | | | | | | {{ txDialog::DIALOG, caption, 0, 0, 0, 240, 85 }, { txDialog::STATIC, text, ID_TEXT_, 10, 10, 150, 40, SS_LEFT }, { txDialog::EDIT, input, ID_INPUT_, 10, 60, 220, 15, ES_LEFT | WS_BORDER | ES_AUTOHSCROLL | WS_TABSTOP }, { txDialog::BUTTON, "&OK", IDOK, 180, 10, 50, 15, BS_DEFPUSHBUTTON | WS_TABSTOP }, { txDialog::BUTTON, "&Cancel", IDCANCEL, 180, 30, 50, 15, BS_PUSHBUTTON | WS_TABSTOP }, { txDialog::END }}; //------------------------------------------------------------------------------------------------------------- // Класс диалога для InputBox. Внутренний, т.к. зачем ему быть внешним. // Нужен в основном для задания строки ввода (str) и оконной функции диалогового окна, требуемой Win32 (она // построена макросами TX_BEGIN_MESSAGE_MAP и другими). Можно не делать внутреннего класса, но тогда оконную // функцию придется писать в глобальной области видимости, и str объявлять глобально тоже (или передавать ее // адрес через DialogBoxParam и записывать его в класс во время обработки WM_INITDIALOG). //------------------------------------------------------------------------------------------------------------- struct inputDlg : txDialog { char str [1024]; //--------------------------------------------------------------------------------------------------------- inputDlg() : str() {} //--------------------------------------------------------------------------------------------------------- TX_BEGIN_MESSAGE_MAP() // Карта сообщений (на самом деле это начало оконной функции). TX_COMMAND_MAP // Здесь обрабатываются WM_COMMAND (на самом деле это оператор switch). //------------------------------------------------------------------------------------------------- // При нажатии кнопки OK копируем строку из поля ввода в нашу переменную str, т.к. после закрытия // диалога строка ввода умрет и текст уже из нее получить. // Этот макрос на самом деле превращается в case из оператора switch. // _wnd -- это параметр оконной функции, см. определение макроса TX_BEGIN_MESSAGE_MAP(). //------------------------------------------------------------------------------------------------- TX_HANDLE (IDOK) GetDlgItemText (_wnd, ID_INPUT_, str, sizeof (str) - 1); TX_END_MESSAGE_MAP //--------------------------------------------------------------------------------------------------------- // Конец внутреннего класса диалога //--------------------------------------------------------------------------------------------------------- }; //------------------------------------------------------------------------------------------------------------- // Убираем дефайны, чтобы потом не мешали. // От этого они получаются "локального действия", как будто у них была бы область видимости -- функция. На самом // деле это сделано вручную через #undef. Чтобы подчеркнуть их локальную природу, у них имена заканчиваются на _. // Такие дефайны потом не перекосячат весь код после того как, фактически, стали уже не нужны. //------------------------------------------------------------------------------------------------------------- #undef ID_TEXT_ #undef ID_INPUT_ //------------------------------------------------------------------------------------------------------------- // Это статический объект, потому что строка в нем должна жить после завершения функции. //------------------------------------------------------------------------------------------------------------- static inputDlg dlg; //------------------------------------------------------------------------------------------------------------- // Передаем layout и запускаем окно диалога //------------------------------------------------------------------------------------------------------------- dlg.dialogBox (layout); //------------------------------------------------------------------------------------------------------------- // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6440  // | | элемента | X | Y |Шир.|Выс.| окон диалога в MSDN)
6441  //----------------------+----------+-----------+---+---+----+----+---------------------------------------------
6442  // | | | | | | |
6443  {{ txDialog::DIALOG, caption, 0, 0, 0, 240, 85 },
6444  { txDialog::STATIC, text, ID_TEXT_, 10, 10, 150, 40, SS_LEFT },
6445  { txDialog::EDIT, input, ID_INPUT_, 10, 60, 220, 15, ES_LEFT | WS_BORDER | ES_AUTOHSCROLL | WS_TABSTOP },
6446  { txDialog::BUTTON, "&OK", IDOK, 180, 10, 50, 15, BS_DEFPUSHBUTTON | WS_TABSTOP },
6447  { txDialog::BUTTON, "&Cancel", IDCANCEL, 180, 30, 50, 15, BS_PUSHBUTTON | WS_TABSTOP },
6448  { txDialog::END }};
6449 
6450  //-------------------------------------------------------------------------------------------------------------
6451  // Класс диалога для InputBox. Внутренний, т.к. зачем ему быть внешним.
6452  // Нужен в основном для задания строки ввода (str) и оконной функции диалогового окна, требуемой Win32 (она // построена макросами TX_BEGIN_MESSAGE_MAP и другими). Можно не делать внутреннего класса, но тогда оконную // функцию придется писать в глобальной области видимости, и str объявлять глобально тоже (или передавать ее // адрес через DialogBoxParam и записывать его в класс во время обработки WM_INITDIALOG). //------------------------------------------------------------------------------------------------------------- struct inputDlg : txDialog { char str [1024]; //--------------------------------------------------------------------------------------------------------- inputDlg() : str() {} //--------------------------------------------------------------------------------------------------------- TX_BEGIN_MESSAGE_MAP() // Карта сообщений (на самом деле это начало оконной функции). TX_COMMAND_MAP // Здесь обрабатываются WM_COMMAND (на самом деле это оператор switch). //------------------------------------------------------------------------------------------------- // При нажатии кнопки OK копируем строку из поля ввода в нашу переменную str, т.к. после закрытия // диалога строка ввода умрет и текст уже из нее получить. // Этот макрос на самом деле превращается в case из оператора switch. // _wnd -- это параметр оконной функции, см. определение макроса TX_BEGIN_MESSAGE_MAP(). //------------------------------------------------------------------------------------------------- TX_HANDLE (IDOK) GetDlgItemText (_wnd, ID_INPUT_, str, sizeof (str) - 1); TX_END_MESSAGE_MAP //--------------------------------------------------------------------------------------------------------- // Конец внутреннего класса диалога //--------------------------------------------------------------------------------------------------------- }; //------------------------------------------------------------------------------------------------------------- // Убираем дефайны, чтобы потом не мешали. // От этого они получаются "локального действия", как будто у них была бы область видимости -- функция. На самом // деле это сделано вручную через #undef. Чтобы подчеркнуть их локальную природу, у них имена заканчиваются на _. // Такие дефайны потом не перекосячат весь код после того как, фактически, стали уже не нужны. //------------------------------------------------------------------------------------------------------------- #undef ID_TEXT_ #undef ID_INPUT_ //------------------------------------------------------------------------------------------------------------- // Это статический объект, потому что строка в нем должна жить после завершения функции. //------------------------------------------------------------------------------------------------------------- static inputDlg dlg; //------------------------------------------------------------------------------------------------------------- // Передаем layout и запускаем окно диалога //------------------------------------------------------------------------------------------------------------- dlg.dialogBox (layout); //------------------------------------------------------------------------------------------------------------- // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6453  // построена макросами TX_BEGIN_MESSAGE_MAP и другими). Можно не делать внутреннего класса, но тогда оконную
6454  // функцию придется писать в глобальной области видимости, и str объявлять глобально тоже (или передавать ее
6455  // адрес через DialogBoxParam и записывать его в класс во время обработки WM_INITDIALOG).
6456  //-------------------------------------------------------------------------------------------------------------
6457  struct inputDlg : txDialog
6458  {
6459  char str [1024];
6460 
6461  //---------------------------------------------------------------------------------------------------------
6462 
6463  inputDlg() :
6464  str()
6465  {}
6466 
6467  //---------------------------------------------------------------------------------------------------------
6468 
6469  TX_BEGIN_MESSAGE_MAP() // Карта сообщений (на самом деле это начало оконной функции).
6470 
6471  TX_COMMAND_MAP // Здесь обрабатываются WM_COMMAND (на самом деле это оператор switch).
6472 
6473  //-------------------------------------------------------------------------------------------------
6474  // При нажатии кнопки OK копируем строку из поля ввода в нашу переменную str, т.к. после закрытия
6475  // диалога строка ввода умрет и текст уже из нее получить.
6476  // Этот макрос на самом деле превращается в case из оператора switch.
6477  // _wnd -- это параметр оконной функции, см. определение макроса TX_BEGIN_MESSAGE_MAP().
6478  //-------------------------------------------------------------------------------------------------
6479 
6480  TX_HANDLE (IDOK) GetDlgItemText (_wnd, ID_INPUT_, str, sizeof (str) - 1);
6481 
6483 
6484  //---------------------------------------------------------------------------------------------------------
6485  // Конец внутреннего класса диалога //--------------------------------------------------------------------------------------------------------- }; //------------------------------------------------------------------------------------------------------------- // Убираем дефайны, чтобы потом не мешали. // От этого они получаются "локального действия", как будто у них была бы область видимости -- функция. На самом // деле это сделано вручную через #undef. Чтобы подчеркнуть их локальную природу, у них имена заканчиваются на _. // Такие дефайны потом не перекосячат весь код после того как, фактически, стали уже не нужны. //------------------------------------------------------------------------------------------------------------- #undef ID_TEXT_ #undef ID_INPUT_ //------------------------------------------------------------------------------------------------------------- // Это статический объект, потому что строка в нем должна жить после завершения функции. //------------------------------------------------------------------------------------------------------------- static inputDlg dlg; //------------------------------------------------------------------------------------------------------------- // Передаем layout и запускаем окно диалога //------------------------------------------------------------------------------------------------------------- dlg.dialogBox (layout); //------------------------------------------------------------------------------------------------------------- // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6486  //---------------------------------------------------------------------------------------------------------
6487  };
6488 
6489  //-------------------------------------------------------------------------------------------------------------
6490  // Убираем дефайны, чтобы потом не мешали.
6491  // От этого они получаются "локального действия", как будто у них была бы область видимости -- функция. На самом // деле это сделано вручную через #undef. Чтобы подчеркнуть их локальную природу, у них имена заканчиваются на _. // Такие дефайны потом не перекосячат весь код после того как, фактически, стали уже не нужны. //------------------------------------------------------------------------------------------------------------- #undef ID_TEXT_ #undef ID_INPUT_ //------------------------------------------------------------------------------------------------------------- // Это статический объект, потому что строка в нем должна жить после завершения функции. //------------------------------------------------------------------------------------------------------------- static inputDlg dlg; //------------------------------------------------------------------------------------------------------------- // Передаем layout и запускаем окно диалога //------------------------------------------------------------------------------------------------------------- dlg.dialogBox (layout); //------------------------------------------------------------------------------------------------------------- // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6492  // деле это сделано вручную через #undef. Чтобы подчеркнуть их локальную природу, у них имена заканчиваются на _.
6493  // Такие дефайны потом не перекосячат весь код после того как, фактически, стали уже не нужны.
6494  //-------------------------------------------------------------------------------------------------------------
6495 
6496  #undef ID_TEXT_
6497  #undef ID_INPUT_
6498 
6499  //-------------------------------------------------------------------------------------------------------------
6500  // Это статический объект, потому что строка в нем должна жить после завершения функции.
6501  //-------------------------------------------------------------------------------------------------------------
6502 
6503  static inputDlg dlg;
6504 
6505  //-------------------------------------------------------------------------------------------------------------
6506  // Передаем layout и запускаем окно диалога //------------------------------------------------------------------------------------------------------------- dlg.dialogBox (layout); //------------------------------------------------------------------------------------------------------------- // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6507  //-------------------------------------------------------------------------------------------------------------
6508 
6509  dlg.dialogBox (layout);
6510 
6511  //-------------------------------------------------------------------------------------------------------------
6512  // Возвращаем адрес строки из статического объекта. Так можно делать, потому что статический объект не умрет // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать // синтаксически можно, но ведет к серьезным ошибкам. //------------------------------------------------------------------------------------------------------------- return dlg.str; } //! @} //} //================================================================================================================= //} //================================================================================================================= //================================================================================================================= //{ TXLIB IMPLEMENTATION // Реализация функций библиотеки //================================================================================================================= //! @cond INTERNAL //================================================================================================================= //{ DLL functions import, missing types definitions //! @name Импорт внешних библиотек, определение недостающих типов //================================================================================================================= //! @{ namespace Win32 { //----------------------------------------------------------------------------------------------------------------- //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers. // Copied from Windows SDK 7.0a. //----------------------------------------------------------------------------------------------------------------- #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #ifndef SMTO_ERRORONEXIT #define SMTO_ERRORONEXIT 0x0020 #endif #ifndef NT_CONSOLE_PROPS_SIG #define NT_CONSOLE_PROPS_SIG 0xA0000002 #endif #ifndef NIIF_INFO #define NIIF_INFO 0x00000001 #define NIIF_WARNING 0x00000002 #define NIIF_ERROR 0x00000003 #endif #ifndef NIF_INFO #define NIF_STATE 0x00000008 #define NIF_INFO 0x00000010 #endif #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 #endif #ifndef SYMOPT_CASE_INSENSITIVE #define SYMOPT_CASE_INSENSITIVE 0x00000001 #define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_NO_CPP 0x00000008 #define SYMOPT_LOAD_LINES 0x00000010 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020 #define SYMOPT_LOAD_ANYTHING 0x00000040 #define SYMOPT_IGNORE_CVREC 0x00000080 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 #define SYMOPT_EXACT_SYMBOLS 0x00000400 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 #define SYMOPT_PUBLICS_ONLY 0x00004000 #define SYMOPT_NO_PUBLICS 0x00008000 #define SYMOPT_AUTO_PUBLICS 0x00010000 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000 #define SYMOPT_SECURE 0x00040000 #define SYMOPT_NO_PROMPTS 0x00080000 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000 #define SYMOPT_FAVOR_COMPRESSED 0x00800000 #define SYMOPT_FLAT_DIRECTORY 0x00400000 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000 #define SYMOPT_OVERWRITE 0x00100000 #define SYMOPT_DEBUG 0x80000000 #endif // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66. #ifndef STATUS_POSSIBLE_DEADLOCK #define STATUS_POSSIBLE_DEADLOCK 0xC0000194 #endif #ifndef STATUS_FLOAT_MULTIPLE_FAULTS #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 #endif #ifndef STATUS_STACK_BUFFER_OVERRUN #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409 #endif #ifndef STATUS_ASSERTION_FAILURE #define STATUS_ASSERTION_FAILURE 0xC0000420 #endif #ifndef STATUS_WX86_BREAKPOINT #define STATUS_WX86_BREAKPOINT 0x4000001F #endif #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call #endif #ifndef DBG_THREAD_NAME #define DBG_THREAD_NAME 0x406D1388 #endif #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc' #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC' #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC' #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC' #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM' #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here #pragma pack (push, 1) struct CONSOLE_CURSOR_INFO { DWORD dwSize; BOOL bVisible; }; struct CONSOLE_FONT_INFO { DWORD nFont; COORD dwFontSize; }; struct CONSOLE_FONT_INFOEX { ULONG cbSize; DWORD nFont; COORD dwFontSize; UINT FontFamily; UINT FontWeight; WCHAR FaceName[LF_FACESIZE]; }; struct DATABLOCK_HEADER { DWORD cbSize; DWORD dwSignature; }; struct NT_CONSOLE_PROPS { DATABLOCK_HEADER dbh; WORD wFillAttribute; WORD wPopupFillAttribute; COORD dwScreenBufferSize; COORD dwWindowSize; COORD dwWindowOrigin; DWORD nFont; DWORD nInputBufferSize; COORD dwFontSize; UINT uFontFamily; UINT uFontWeight; WCHAR FaceName[LF_FACESIZE]; UINT uCursorSize; BOOL bFullScreen; BOOL bQuickEdit; BOOL bInsertMode; BOOL bAutoPosition; UINT uHistoryBufferSize; UINT uNumberOfHistoryBuffers; BOOL bHistoryNoDup; COLORREF ColorTable[16]; }; #pragma pack (pop) #undef INTERFACE #define INTERFACE IShellLinkDataList DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown) { // *** IUnknown methods *** STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE; STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE; STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE; // *** IShellLinkDataList methods *** STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE; STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE; STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE; STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE; STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE; protected: virtual ~IShellLinkDataList(); }; const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}}; const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}}; const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}}; #undef INTERFACE typedef DWORD NTSTATUS; typedef ULONG_PTR KAFFINITY; typedef LONG KPRIORITY; struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; WCHAR* Buffer; }; struct RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; void* Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; }; struct PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; void* Reserved3[2]; void* Ldr; RTL_USER_PROCESS_PARAMETERS* ProcessParameters; void* Reserved4[3]; void* AtlThunkSListPtr; void* Reserved5; ULONG Reserved6; void* Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; void* Reserved9[45]; BYTE Reserved10[96]; void* PostProcessInitRoutine; BYTE Reserved11[128]; void* Reserved12[1]; ULONG SessionId; }; struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PEB* PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; enum ADDRESS_MODE { AddrMode1616, AddrMode1632, AddrModeReal, AddrModeFlat }; struct ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; }; struct KDHELP64 { DWORD64 Thread; DWORD ThCallbackStack; DWORD ThCallbackBStore; DWORD NextCallback; DWORD FramePointer; DWORD64 KiCallUserMode; DWORD64 KeUserCallbackDispatcher; DWORD64 SystemRangeStart; DWORD64 KiUserExceptionDispatcher; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 Reserved[5]; }; struct STACKFRAME64 { ADDRESS64 AddrPC; ADDRESS64 AddrReturn; ADDRESS64 AddrFrame; ADDRESS64 AddrStack; ADDRESS64 AddrBStore; PVOID FuncTableEntry; DWORD64 Params[4]; BOOL Far; BOOL Virtual; DWORD64 Reserved[3]; KDHELP64 KdHelp; }; struct WOW64_FLOATING_SAVE_AREA { DWORD ControlWord; DWORD StatusWord; DWORD TagWord; DWORD ErrorOffset; DWORD ErrorSelector; DWORD DataOffset; DWORD DataSelector; BYTE RegisterArea[80]; DWORD Cr0NpxState; }; #pragma pack (push, 4) struct WOW64_CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; WOW64_FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[512]; }; #pragma pack (pop) struct SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; }; struct IMAGEHLP_LINE64 { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD64 Address; }; typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead); typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress); typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address); typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address); typedef void (*unexpected_handler)(); #pragma pack (push, 4) struct MINIDUMP_THREAD_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; }; struct MINIDUMP_THREAD_EX_CALLBACK { ULONG ThreadId; HANDLE ThreadHandle; CONTEXT Context; ULONG SizeOfContext; ULONG64 StackBase; ULONG64 StackEnd; ULONG64 BackingStoreBase; ULONG64 BackingStoreEnd; }; struct MINIDUMP_MODULE_CALLBACK { wchar_t* FullPath; ULONG64 BaseOfImage; ULONG SizeOfImage; ULONG CheckSum; ULONG TimeDateStamp; VS_FIXEDFILEINFO VersionInfo; void* CvRecord; ULONG SizeOfCvRecord; PVOID MiscRecord; ULONG SizeOfMiscRecord; }; struct MINIDUMP_INCLUDE_THREAD_CALLBACK { ULONG ThreadId; }; struct MINIDUMP_INCLUDE_MODULE_CALLBACK { ULONG64 BaseOfImage; }; struct MINIDUMP_MEMORY_INFO { ULONG64 BaseAddress; ULONG64 AllocationBase; ULONG32 AllocationProtect; ULONG32 __alignment1; ULONG64 RegionSize; ULONG32 State; ULONG32 Protect; ULONG32 Type; ULONG32 __alignment2; }; struct MINIDUMP_USER_STREAM { ULONG32 Type; ULONG BufferSize; PVOID Buffer; }; struct MINIDUMP_USER_STREAM_INFORMATION { ULONG UserStreamCount; MINIDUMP_USER_STREAM* UserStreamArray; }; struct MINIDUMP_CALLBACK_INPUT { ULONG ProcessId; HANDLE ProcessHandle; ULONG CallbackType; union { MINIDUMP_THREAD_CALLBACK Thread; MINIDUMP_THREAD_EX_CALLBACK ThreadEx; MINIDUMP_MODULE_CALLBACK Module; MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread; MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule; }; }; struct MINIDUMP_CALLBACK_OUTPUT { union { ULONG ModuleWriteFlags; ULONG ThreadWriteFlags; ULONG SecondaryFlags; struct { ULONG64 MemoryBase; ULONG MemorySize; }; struct { unsigned CheckCancel; unsigned Cancel; }; HANDLE Handle; }; struct { MINIDUMP_MEMORY_INFO VmRegion; unsigned Continue; }; HRESULT Status; }; struct MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; EXCEPTION_POINTERS* ExceptionPointers; unsigned ClientPointers; }; typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output); struct MINIDUMP_CALLBACK_INFORMATION { MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; void* CallbackParam; }; enum MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutAuxiliaryState = 0x00004000, MiniDumpWithFullAuxiliaryState = 0x00008000, MiniDumpWithPrivateWriteCopyMemory = 0x00010000, MiniDumpIgnoreInaccessibleMemory = 0x00020000, MiniDumpWithTokenInformation = 0x00040000 }; #pragma pack (pop) #define FOREGROUND_BLACK ( 0 ) #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN ) #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED ) #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ) #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY ) #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY ) #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY ) #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY ) #define BACKGROUND_BLACK ( 0 ) #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN ) #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED ) #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED ) #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY ) #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY ) #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY ) } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode. // So we have to override them. See: http://stackoverflow.com/questions/39113168 //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) // MS ABI C++ Exception Layout namespace Win32 { // --------------------------- // #pragma pack (push, 4) // EXCEPTION_RECORD: // +==================================================+ struct ThrowInfo // |... | { // |NumberParameters: 3, 4 or more | __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 | __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown | __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+ __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| | }; // +==================================================+ | // | struct CatchableTypeArray // ThrowInfo: | { // +======================================+ <-------+ __int32 nCatchableTypes; // | ... | __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) | }; // | +======================================+ // | struct CatchableType // | CatchableTypeArray: { // +---> +======================================+ __int32 properties; // | ... | __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) | __int32 thisDisplacement[3]; // struct _PMD // | +======================================+ __int32 sizeOrOffset; // | __int32 copyFunction; // | CatchableType: }; // +---> +====================+ // | ... | std::type_info: #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+ // | ... | |type_info data | } // namespace Win32 // +====================+ |... | // +==================+ #endif // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp: #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \ ( \ (exc) && \ (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \ (exc) -> NumberParameters >= 3 && \ \ ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \ (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \ \ (exc) -> ExceptionInformation[2] == 0 \ ) //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ The corresponding structures for GCC // // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi //----------------------------------------------------------------------------------------------------------------- #if defined (_GCC_VER) // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0]. namespace ABI { // -------------------------------------------------------------- // struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception: { // -------------------------------------------------------- union { // struct // __cxa_exception: std::type_info: { // -*--+====================+ +==================+ ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data | void (*exceptionDestructor)(void*); // | |... | |... | }; // -1 | | +==================+ struct // | | | { // A >----|--+--------------------+ __cxa_exception* primaryException; // | | |unwindHeader | void (*padding)(); // +1 | | | }; // | | | | }; // V | | | // -*--- +====================+ std::unexpected_handler unexpectedHandler; // |object | std::terminate_handler terminateHandler; // +--------------------+ // __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception int handlerCount; // (unwindHeader.exception_class & 1 != 0): int handlerSwitchValue; // ----------------------------------------------------- const unsigned char* actionRecord; // const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception: void* catchTemp; // -*--+====================+ -*--+=================+ void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* | // | |... | \ | |... | _Unwind_Exception unwindHeader; // -1 | | | | | | }; // | | | | | | | // B >----|--+--------------------+ | -1 +-----------------+ struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader | { // +1 | | | | | | | __cxa_exception* caughtExceptions; // | | | | | | | | unsigned int uncaughtExceptions; // V | | | \ | | | }; // -*--- +====================+ -->*--+=================+ // |... | |object | } // namespace ABI // . . | | // +-----------------+ extern "C" ABI::__cxa_eh_globals* __cxa_get_globals(); #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Hand-made IAT. // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :( //----------------------------------------------------------------------------------------------------------------- // Hand-made DLLIMPORT helpers #define _TX_DLLIMPORT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true) #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \ retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false) #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \ retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false) typedef void (*_tx_FARPROC)(); _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true); //----------------------------------------------------------------------------------------------------------------- namespace Win32 { _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType)); _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer)); _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc)); _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object)); _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color)); _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode)); _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation, int weight, DWORD italic, DWORD underline, DWORD strikeout, DWORD charSet, DWORD outputPrec, DWORD clipPrec, DWORD quality, DWORD pitchAndFamily, const char face[])); _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc, LPARAM lParam, DWORD reserved)); _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color)); _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color)); _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color)); _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point)); _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y)); _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count)); _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY)); _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1)); _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1, int xStart, int yStart, int xEnd, int yEnd)); _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length)); _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode)); _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size)); _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type)); _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)); _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram, HDC src, int xSrc, int ySrc, int width, int height, HBITMAP mask, int xMask, int yMask)); _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height, int xSrc, int ySrc, unsigned startLine, unsigned numLines, const void* data, const BITMAPINFO* info, unsigned colorUse)); _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines, void* lpvBits, BITMAPINFO* lpbi, unsigned usage)); _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp)); _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode)); _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit)); _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits, HANDLE section, DWORD offset)); _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format)); _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type, int sizex, int sizey, unsigned mode)); _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd)); _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd)); _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode)); _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, unsigned transparentColor)); _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight, HDC src, int srcX, int srcY, int srcWidth, int srcHeight, BLENDFUNCTION blending)); _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode)); _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode)); _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[])); _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex)); _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont)); _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord)); _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash)); _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler)); _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler)); _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module)); _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process)); _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context)); _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize)); _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*)); _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value)); _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void)); _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[], const char parameters[], const char directory[], int showCmd)); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[])); _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[])); _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void)); _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass, void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo)); _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode)); _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void)); _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist)); _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*), void *arglist, unsigned init_flag, unsigned* thread_addr)); _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen, void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer), unsigned short flags)); _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler)); _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void)); _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void)); _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType, MINIDUMP_EXCEPTION_INFORMATION* exceptionParam, MINIDUMP_USER_STREAM_INFORMATION* userStreamParam, MINIDUMP_CALLBACK_INFORMATION* callbackParam)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); namespace MinGW { _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line)); _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process)); _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase)); _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord, PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc, PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc, PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc, PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc)); } // namespace MinGW } // namespace Win32 //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal function prototypes, macros and constants // @name Прототипы внутренних функций, макросы и константы //================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
6513  // при выходе из функции и строка в нем, соответственно, тоже. Адрес нестатических переменных передавать
6514  // синтаксически можно, но ведет к серьезным ошибкам.
6515  //-------------------------------------------------------------------------------------------------------------
6516 
6517  return dlg.str;
6518  }
6519 
6521 //}
6522 //=================================================================================================================
6523 
6524 //}
6525 //=================================================================================================================
6526 
6527 //=================================================================================================================
6528 //{ TXLIB IMPLEMENTATION
6529 // Реализация функций библиотеки
6530 //=================================================================================================================
6532 
6533 //=================================================================================================================
6534 //{ DLL functions import, missing types definitions
6536 //=================================================================================================================
6538 
6539 namespace Win32 {
6540 
6541 //-----------------------------------------------------------------------------------------------------------------
6542 //{ Some of structs, consts and interfaces aren't defined in MinGW some early headers.
6543 // Copied from Windows SDK 7.0a.
6544 //-----------------------------------------------------------------------------------------------------------------
6545 
6546 #ifndef AC_SRC_ALPHA
6547 #define AC_SRC_ALPHA 0x01
6548 #endif
6549 
6550 #ifndef SMTO_ERRORONEXIT
6551 #define SMTO_ERRORONEXIT 0x0020
6552 #endif
6553 
6554 #ifndef NT_CONSOLE_PROPS_SIG
6555 #define NT_CONSOLE_PROPS_SIG 0xA0000002
6556 #endif
6557 
6558 #ifndef NIIF_INFO
6559 #define NIIF_INFO 0x00000001
6560 #define NIIF_WARNING 0x00000002
6561 #define NIIF_ERROR 0x00000003
6562 #endif
6563 
6564 #ifndef NIF_INFO
6565 #define NIF_STATE 0x00000008
6566 #define NIF_INFO 0x00000010
6567 #endif
6568 
6569 #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
6570 #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004
6571 #endif
6572 
6573 #ifndef SYMOPT_CASE_INSENSITIVE
6574 #define SYMOPT_CASE_INSENSITIVE 0x00000001
6575 #define SYMOPT_UNDNAME 0x00000002
6576 #define SYMOPT_DEFERRED_LOADS 0x00000004
6577 #define SYMOPT_NO_CPP 0x00000008
6578 #define SYMOPT_LOAD_LINES 0x00000010
6579 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020
6580 #define SYMOPT_LOAD_ANYTHING 0x00000040
6581 #define SYMOPT_IGNORE_CVREC 0x00000080
6582 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
6583 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
6584 #define SYMOPT_EXACT_SYMBOLS 0x00000400
6585 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
6586 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
6587 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
6588 #define SYMOPT_PUBLICS_ONLY 0x00004000
6589 #define SYMOPT_NO_PUBLICS 0x00008000
6590 #define SYMOPT_AUTO_PUBLICS 0x00010000
6591 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000
6592 #define SYMOPT_SECURE 0x00040000
6593 #define SYMOPT_NO_PROMPTS 0x00080000
6594 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000
6595 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000
6596 #define SYMOPT_FAVOR_COMPRESSED 0x00800000
6597 #define SYMOPT_FLAT_DIRECTORY 0x00400000
6598 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000
6599 #define SYMOPT_OVERWRITE 0x00100000
6600 #define SYMOPT_DEBUG 0x80000000
6601 #endif
6602 
6603 // SEH exception codes. For GCC, see http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 64-66.
6604 
6605 #ifndef STATUS_POSSIBLE_DEADLOCK
6606 #define STATUS_POSSIBLE_DEADLOCK 0xC0000194
6607 #endif
6608 
6609 #ifndef STATUS_FLOAT_MULTIPLE_FAULTS
6610 #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4
6611 #endif
6612 
6613 #ifndef STATUS_STACK_BUFFER_OVERRUN
6614 #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409
6615 #endif
6616 
6617 #ifndef STATUS_ASSERTION_FAILURE
6618 #define STATUS_ASSERTION_FAILURE 0xC0000420
6619 #endif
6620 
6621 #ifndef STATUS_WX86_BREAKPOINT
6622 #define STATUS_WX86_BREAKPOINT 0x4000001F
6623 #endif
6624 
6625 #ifndef DBG_PRINTEXCEPTION_C
6626 #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call
6627 #endif
6628 
6629 #ifndef DBG_PRINTEXCEPTION_WIDE_C
6630 #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call
6631 #endif
6632 
6633 #ifndef DBG_THREAD_NAME
6634 #define DBG_THREAD_NAME 0x406D1388
6635 #endif
6636 
6637 #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc'
6638 #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h
6639 #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic
6640 #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic
6641 #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic
6642 
6643 #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC'
6644 #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC'
6645 #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC'
6646 
6647 #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM'
6648 
6649 #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here
6650 #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here
6651 
6652 #pragma pack (push, 1)
6653 
6654 struct CONSOLE_CURSOR_INFO
6655  {
6656  DWORD dwSize;
6657  BOOL bVisible;
6658  };
6659 
6660 struct CONSOLE_FONT_INFO
6661  {
6662  DWORD nFont;
6663  COORD dwFontSize;
6664  };
6665 
6666 struct CONSOLE_FONT_INFOEX
6667  {
6668  ULONG cbSize;
6669  DWORD nFont;
6670  COORD dwFontSize;
6671  UINT FontFamily;
6672  UINT FontWeight;
6673  WCHAR FaceName[LF_FACESIZE];
6674  };
6675 
6676 struct DATABLOCK_HEADER
6677  {
6678  DWORD cbSize;
6679  DWORD dwSignature;
6680  };
6681 
6682 struct NT_CONSOLE_PROPS
6683  {
6684  DATABLOCK_HEADER dbh;
6685 
6686  WORD wFillAttribute;
6687  WORD wPopupFillAttribute;
6688  COORD dwScreenBufferSize;
6689  COORD dwWindowSize;
6690  COORD dwWindowOrigin;
6691  DWORD nFont;
6692  DWORD nInputBufferSize;
6693  COORD dwFontSize;
6694  UINT uFontFamily;
6695  UINT uFontWeight;
6696  WCHAR FaceName[LF_FACESIZE];
6697  UINT uCursorSize;
6698  BOOL bFullScreen;
6699  BOOL bQuickEdit;
6700  BOOL bInsertMode;
6701  BOOL bAutoPosition;
6702  UINT uHistoryBufferSize;
6703  UINT uNumberOfHistoryBuffers;
6704  BOOL bHistoryNoDup;
6705 
6706  COLORREF ColorTable[16];
6707  };
6708 
6709 #pragma pack (pop)
6710 
6711 #undef INTERFACE
6712 #define INTERFACE IShellLinkDataList
6713 
6714 DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown)
6715  {
6716  // *** IUnknown methods ***
6717  STDMETHOD (QueryInterface) (THIS_ REFIID iid, void** value) _tx_override PURE;
6718  STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE;
6719  STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE;
6720 
6721  // *** IShellLinkDataList methods ***
6722  STDMETHOD (AddDataBlock) (THIS_ void* dataBlock) PURE;
6723  STDMETHOD (CopyDataBlock) (THIS_ DWORD sig, void** dataBlock) PURE;
6724  STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE;
6725  STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE;
6726  STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE;
6727 
6728  protected:
6729  virtual ~IShellLinkDataList();
6730  };
6731 
6732 const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
6733 const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}};
6734 const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
6735 
6736 const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}};
6737 const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}};
6738 
6739 #undef INTERFACE
6740 
6741 typedef DWORD NTSTATUS;
6742 typedef ULONG_PTR KAFFINITY;
6743 typedef LONG KPRIORITY;
6744 
6745 struct UNICODE_STRING
6746  {
6747  USHORT Length;
6748  USHORT MaximumLength;
6749  WCHAR* Buffer;
6750  };
6751 
6752 struct RTL_USER_PROCESS_PARAMETERS
6753  {
6754  BYTE Reserved1[16];
6755  void* Reserved2[10];
6756  UNICODE_STRING ImagePathName;
6757  UNICODE_STRING CommandLine;
6758  };
6759 
6760 struct PEB
6761  {
6762  BYTE Reserved1[2];
6763  BYTE BeingDebugged;
6764  BYTE Reserved2[1];
6765  void* Reserved3[2];
6766  void* Ldr;
6767  RTL_USER_PROCESS_PARAMETERS* ProcessParameters;
6768  void* Reserved4[3];
6769  void* AtlThunkSListPtr;
6770  void* Reserved5;
6771  ULONG Reserved6;
6772  void* Reserved7;
6773  ULONG Reserved8;
6774  ULONG AtlThunkSListPtr32;
6775  void* Reserved9[45];
6776  BYTE Reserved10[96];
6777  void* PostProcessInitRoutine;
6778  BYTE Reserved11[128];
6779  void* Reserved12[1];
6780  ULONG SessionId;
6781  };
6782 
6783 struct PROCESS_BASIC_INFORMATION
6784  {
6785  NTSTATUS ExitStatus;
6786  PEB* PebBaseAddress;
6787  KAFFINITY AffinityMask;
6788  KPRIORITY BasePriority;
6789  ULONG_PTR UniqueProcessId;
6790  ULONG_PTR InheritedFromUniqueProcessId;
6791  };
6792 
6793 enum ADDRESS_MODE
6794  {
6795  AddrMode1616,
6796  AddrMode1632,
6797  AddrModeReal,
6798  AddrModeFlat
6799  };
6800 
6801 struct ADDRESS64
6802  {
6803  DWORD64 Offset;
6804  WORD Segment;
6805  ADDRESS_MODE Mode;
6806  };
6807 
6808 struct KDHELP64
6809  {
6810  DWORD64 Thread;
6811  DWORD ThCallbackStack;
6812  DWORD ThCallbackBStore;
6813  DWORD NextCallback;
6814  DWORD FramePointer;
6815  DWORD64 KiCallUserMode;
6816  DWORD64 KeUserCallbackDispatcher;
6817  DWORD64 SystemRangeStart;
6818  DWORD64 KiUserExceptionDispatcher;
6819  DWORD64 StackBase;
6820  DWORD64 StackLimit;
6821  DWORD64 Reserved[5];
6822  };
6823 
6824 struct STACKFRAME64
6825  {
6826  ADDRESS64 AddrPC;
6827  ADDRESS64 AddrReturn;
6828  ADDRESS64 AddrFrame;
6829  ADDRESS64 AddrStack;
6830  ADDRESS64 AddrBStore;
6831  PVOID FuncTableEntry;
6832  DWORD64 Params[4];
6833  BOOL Far;
6834  BOOL Virtual;
6835  DWORD64 Reserved[3];
6836  KDHELP64 KdHelp;
6837  };
6838 
6839 struct WOW64_FLOATING_SAVE_AREA
6840  {
6841  DWORD ControlWord;
6842  DWORD StatusWord;
6843  DWORD TagWord;
6844  DWORD ErrorOffset;
6845  DWORD ErrorSelector;
6846  DWORD DataOffset;
6847  DWORD DataSelector;
6848  BYTE RegisterArea[80];
6849  DWORD Cr0NpxState;
6850  };
6851 
6852 #pragma pack (push, 4)
6853 
6854 struct WOW64_CONTEXT
6855  {
6856  DWORD ContextFlags;
6857 
6858  DWORD Dr0;
6859  DWORD Dr1;
6860  DWORD Dr2;
6861  DWORD Dr3;
6862  DWORD Dr6;
6863  DWORD Dr7;
6864 
6865  WOW64_FLOATING_SAVE_AREA FloatSave;
6866 
6867  DWORD SegGs;
6868  DWORD SegFs;
6869  DWORD SegEs;
6870  DWORD SegDs;
6871 
6872  DWORD Edi;
6873  DWORD Esi;
6874  DWORD Ebx;
6875  DWORD Edx;
6876  DWORD Ecx;
6877  DWORD Eax;
6878 
6879  DWORD Ebp;
6880  DWORD Eip;
6881  DWORD SegCs;
6882  DWORD EFlags;
6883  DWORD Esp;
6884  DWORD SegSs;
6885 
6886  BYTE ExtendedRegisters[512];
6887  };
6888 
6889 #pragma pack (pop)
6890 
6891 struct SYMBOL_INFO
6892  {
6893  ULONG SizeOfStruct;
6894  ULONG TypeIndex;
6895  ULONG64 Reserved[2];
6896  ULONG info;
6897  ULONG Size;
6898  ULONG64 ModBase;
6899  ULONG Flags;
6900  ULONG64 Value;
6901  ULONG64 Address;
6902  ULONG Register;
6903  ULONG Scope;
6904  ULONG Tag;
6905  ULONG NameLen;
6906  ULONG MaxNameLen;
6907  CHAR Name[1];
6908  };
6909 
6910 struct IMAGEHLP_LINE64
6911  {
6912  DWORD SizeOfStruct;
6913  PVOID Key;
6914  DWORD LineNumber;
6915  PCHAR FileName;
6916  DWORD64 Address;
6917  };
6918 
6919 typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress, void* buffer, DWORD size, DWORD* bytesRead);
6920 typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress);
6921 typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address);
6922 typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address);
6923 
6924 typedef void (*unexpected_handler)();
6925 
6926 #pragma pack (push, 4)
6927 
6928 struct MINIDUMP_THREAD_CALLBACK
6929  {
6930  ULONG ThreadId;
6931  HANDLE ThreadHandle;
6932  CONTEXT Context;
6933  ULONG SizeOfContext;
6934  ULONG64 StackBase;
6935  ULONG64 StackEnd;
6936  };
6937 
6938 struct MINIDUMP_THREAD_EX_CALLBACK
6939  {
6940  ULONG ThreadId;
6941  HANDLE ThreadHandle;
6942  CONTEXT Context;
6943  ULONG SizeOfContext;
6944  ULONG64 StackBase;
6945  ULONG64 StackEnd;
6946  ULONG64 BackingStoreBase;
6947  ULONG64 BackingStoreEnd;
6948  };
6949 
6950 struct MINIDUMP_MODULE_CALLBACK
6951  {
6952  wchar_t* FullPath;
6953  ULONG64 BaseOfImage;
6954  ULONG SizeOfImage;
6955  ULONG CheckSum;
6956  ULONG TimeDateStamp;
6957  VS_FIXEDFILEINFO VersionInfo;
6958  void* CvRecord;
6959  ULONG SizeOfCvRecord;
6960  PVOID MiscRecord;
6961  ULONG SizeOfMiscRecord;
6962  };
6963 
6964 struct MINIDUMP_INCLUDE_THREAD_CALLBACK
6965  {
6966  ULONG ThreadId;
6967  };
6968 
6969 struct MINIDUMP_INCLUDE_MODULE_CALLBACK
6970  {
6971  ULONG64 BaseOfImage;
6972  };
6973 
6974 struct MINIDUMP_MEMORY_INFO
6975  {
6976  ULONG64 BaseAddress;
6977  ULONG64 AllocationBase;
6978  ULONG32 AllocationProtect;
6979  ULONG32 __alignment1;
6980  ULONG64 RegionSize;
6981  ULONG32 State;
6982  ULONG32 Protect;
6983  ULONG32 Type;
6984  ULONG32 __alignment2;
6985  };
6986 
6987 struct MINIDUMP_USER_STREAM
6988  {
6989  ULONG32 Type;
6990  ULONG BufferSize;
6991  PVOID Buffer;
6992  };
6993 
6994 struct MINIDUMP_USER_STREAM_INFORMATION
6995  {
6996  ULONG UserStreamCount;
6997  MINIDUMP_USER_STREAM* UserStreamArray;
6998  };
6999 
7000 struct MINIDUMP_CALLBACK_INPUT
7001  {
7002  ULONG ProcessId;
7003  HANDLE ProcessHandle;
7004  ULONG CallbackType;
7005 
7006  union
7007  {
7008  MINIDUMP_THREAD_CALLBACK Thread;
7009  MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
7010  MINIDUMP_MODULE_CALLBACK Module;
7011  MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
7012  MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
7013  };
7014  };
7015 
7016 struct MINIDUMP_CALLBACK_OUTPUT
7017  {
7018  union
7019  {
7020  ULONG ModuleWriteFlags;
7021  ULONG ThreadWriteFlags;
7022  ULONG SecondaryFlags;
7023 
7024  struct
7025  {
7026  ULONG64 MemoryBase;
7027  ULONG MemorySize;
7028  };
7029 
7030  struct
7031  {
7032  unsigned CheckCancel;
7033  unsigned Cancel;
7034  };
7035 
7036  HANDLE Handle;
7037  };
7038 
7039  struct
7040  {
7041  MINIDUMP_MEMORY_INFO VmRegion;
7042  unsigned Continue;
7043  };
7044 
7045  HRESULT Status;
7046  };
7047 
7048 struct MINIDUMP_EXCEPTION_INFORMATION
7049  {
7050  DWORD ThreadId;
7051  EXCEPTION_POINTERS* ExceptionPointers;
7052  unsigned ClientPointers;
7053  };
7054 
7055 typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output);
7056 
7057 struct MINIDUMP_CALLBACK_INFORMATION
7058  {
7059  MINIDUMP_CALLBACK_ROUTINE CallbackRoutine;
7060  void* CallbackParam;
7061  };
7062 
7063 enum MINIDUMP_TYPE
7064  {
7065  MiniDumpNormal = 0x00000000,
7066  MiniDumpWithDataSegs = 0x00000001,
7067  MiniDumpWithFullMemory = 0x00000002,
7068  MiniDumpWithHandleData = 0x00000004,
7069  MiniDumpFilterMemory = 0x00000008,
7070  MiniDumpScanMemory = 0x00000010,
7071  MiniDumpWithUnloadedModules = 0x00000020,
7072  MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
7073  MiniDumpFilterModulePaths = 0x00000080,
7074  MiniDumpWithProcessThreadData = 0x00000100,
7075  MiniDumpWithPrivateReadWriteMemory = 0x00000200,
7076  MiniDumpWithoutOptionalData = 0x00000400,
7077  MiniDumpWithFullMemoryInfo = 0x00000800,
7078  MiniDumpWithThreadInfo = 0x00001000,
7079  MiniDumpWithCodeSegs = 0x00002000,
7080  MiniDumpWithoutAuxiliaryState = 0x00004000,
7081  MiniDumpWithFullAuxiliaryState = 0x00008000,
7082  MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
7083  MiniDumpIgnoreInaccessibleMemory = 0x00020000,
7084  MiniDumpWithTokenInformation = 0x00040000
7085  };
7086 
7087 #pragma pack (pop)
7088 
7089 #define FOREGROUND_BLACK ( 0 )
7090 #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN )
7091 #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED )
7092 #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED )
7093 #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED )
7094 #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY )
7095 #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY )
7096 #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY )
7097 #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY )
7098 #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY )
7099 #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY )
7100 #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY )
7101 #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY )
7102 
7103 #define BACKGROUND_BLACK ( 0 )
7104 #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN )
7105 #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED )
7106 #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED )
7107 #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED )
7108 #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY )
7109 #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY )
7110 #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY )
7111 #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY )
7112 #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY )
7113 #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY )
7114 #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY )
7115 #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY )
7116 
7117 } // namespace Win32
7118 
7119 //}
7120 //-----------------------------------------------------------------------------------------------------------------
7121 
7122 //-----------------------------------------------------------------------------------------------------------------
7123 //{ There are copies of MSVC compiler built-in predefined definitions, which are wrong in 64-bit mode.
7124 // So we have to override them. See: http://stackoverflow.com/questions/39113168
7125 //-----------------------------------------------------------------------------------------------------------------
7126 
7127 #if defined (_MSC_VER)
7128  // MS ABI C++ Exception Layout
7129 namespace Win32 { // ---------------------------
7130  //
7131 #pragma pack (push, 4) // EXCEPTION_RECORD:
7132  // +==================================================+
7133 struct ThrowInfo // |... |
7134  { // |NumberParameters: 3, 4 or more |
7135  __int32 attributes; // |ExceptionInformation[0]: MS signature 0x19930520 |
7136  __int32 pmfnUnwind; // |ExceptionInformation[1]: object* thrown |
7137  __int32 pForwardCompat; // |ExceptionInformation[2]: ThrowInfo* --------------+---+
7138  __int32 pCatchableTypeArray; // |ExceptionInformation[3]: ImageBase (if params > 3)| |
7139  }; // +==================================================+ |
7140  // |
7141 struct CatchableTypeArray // ThrowInfo: |
7142  { // +======================================+ <-------+
7143  __int32 nCatchableTypes; // | ... |
7144  __int32 arrayOfCatchableTypes[]; // +-----+-- pCatchableTypeArray (ptr/RVA) |
7145  }; // | +======================================+
7146  // |
7147 struct CatchableType // | CatchableTypeArray:
7148  { // +---> +======================================+
7149  __int32 properties; // | ... |
7150  __int32 pType; // +-----+-- arrayOfCatchableTypes[0] (ptr/RVA) |
7151  __int32 thisDisplacement[3]; // struct _PMD // | +======================================+
7152  __int32 sizeOrOffset; // |
7153  __int32 copyFunction; // | CatchableType:
7154  }; // +---> +====================+
7155  // | ... | std::type_info:
7156 #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+
7157  // | ... | |type_info data |
7158 } // namespace Win32 // +====================+ |... |
7159  // +==================+
7160 #endif
7161 
7162 // Similar to __CxxDetectRethrow(), see C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\crtsrc\vcruntime\frame.cpp:
7163 
7164 #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \
7165  ( \
7166  (exc) && \
7167  (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \
7168  (exc) -> NumberParameters >= 3 && \
7169  \
7170  ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \
7171  (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \
7172  (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \
7173  \
7174  (exc) -> ExceptionInformation[2] == 0 \
7175  )
7176 
7177 //}
7178 //-----------------------------------------------------------------------------------------------------------------
7179 
7180 //-----------------------------------------------------------------------------------------------------------------
7181 //{ The corresponding structures for GCC
7182 //
7183 // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h
7184 // See: http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi
7185 //-----------------------------------------------------------------------------------------------------------------
7186 
7187 #if defined (_GCC_VER)
7188  // GCC ABI C++ Exception layout. A/B are ExceptionInformation[0].
7189 namespace ABI { // --------------------------------------------------------------
7190  //
7191 struct __cxa_exception // Case A: "_Unwind_Exception* A" is undependent exception:
7192  { // --------------------------------------------------------
7193  union { //
7194  struct // __cxa_exception: std::type_info:
7195  { // -*--+====================+ +==================+
7196  ::std::type_info* exceptionType; // ^ |exceptionType* -----+---->|type_info data |
7197  void (*exceptionDestructor)(void*); // | |... | |... |
7198  }; // -1 | | +==================+
7199  struct // | | |
7200  { // A >----|--+--------------------+
7201  __cxa_exception* primaryException; // | | |unwindHeader |
7202  void (*padding)(); // +1 | | |
7203  }; // | | | |
7204  }; // V | | |
7205  // -*--- +====================+
7206  std::unexpected_handler unexpectedHandler; // |object |
7207  std::terminate_handler terminateHandler; // +--------------------+
7208  //
7209  __cxa_exception* nextException; // Case B: "_Unwind_Exception* B" is dependent exception
7210  int handlerCount; // (unwindHeader.exception_class & 1 != 0):
7211  int handlerSwitchValue; // -----------------------------------------------------
7212  const unsigned char* actionRecord; //
7213  const unsigned char* languageSpecificData; // __cxa_exception: __cxa_exception:
7214  void* catchTemp; // -*--+====================+ -*--+=================+
7215  void* adjustedPtr; // ^ |primaryException* --+-- ^ |exceptionType* |
7216  // | |... | \ | |... |
7217  _Unwind_Exception unwindHeader; // -1 | | | | | |
7218  }; // | | | | | | |
7219  // B >----|--+--------------------+ | -1 +-----------------+
7220 struct __cxa_eh_globals // | | |unwindHeader | | | |unwindHeader |
7221  { // +1 | | | | | | |
7222  __cxa_exception* caughtExceptions; // | | | | | | | |
7223  unsigned int uncaughtExceptions; // V | | | \ | | |
7224  }; // -*--- +====================+ -->*--+=================+
7225  // |... | |object |
7226 } // namespace ABI // . . | |
7227  // +-----------------+
7228 
7229 extern "C" ABI::__cxa_eh_globals* __cxa_get_globals();
7230 
7231 #endif
7232 
7233 //}
7234 //-----------------------------------------------------------------------------------------------------------------
7235 
7236 //-----------------------------------------------------------------------------------------------------------------
7237 //{ Hand-made IAT.
7238 // Some IDEs don't link with these libs by default in console projects, so do sunrise by hand. :(
7239 //-----------------------------------------------------------------------------------------------------------------
7240 
7241 // Hand-made DLLIMPORT helpers
7242 
7243 #define _TX_DLLIMPORT( lib, retval, name, params ) \
7244  retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true)
7245 
7246 #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \
7247  retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false)
7248 
7249 #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \
7250  retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false)
7251 
7252 typedef void (*_tx_FARPROC)();
7253 
7254 _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required = true);
7255 
7256 //-----------------------------------------------------------------------------------------------------------------
7257 
7258 namespace Win32 {
7259 
7260 _TX_DLLIMPORT ("GDI32", HDC, CreateCompatibleDC, (HDC dc));
7261 _TX_DLLIMPORT ("GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc, int width, int height));
7262 _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetStockObject, (int object));
7263 _TX_DLLIMPORT ("GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ object));
7264 _TX_DLLIMPORT ("GDI32", HGDIOBJ, GetCurrentObject, (HDC dc, unsigned objectType));
7265 _TX_DLLIMPORT ("GDI32", int, GetObjectA, (HGDIOBJ obj, int bufsize, void* buffer));
7266 _TX_DLLIMPORT ("GDI32", DWORD, GetObjectType, (HGDIOBJ object));
7267 _TX_DLLIMPORT ("GDI32", bool, DeleteDC, (HDC dc));
7268 _TX_DLLIMPORT ("GDI32", bool, DeleteObject, (HGDIOBJ object));
7269 _TX_DLLIMPORT ("GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color));
7270 _TX_DLLIMPORT ("GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color));
7271 _TX_DLLIMPORT ("GDI32", int, SetBkMode, (HDC dc, int bkMode));
7272 _TX_DLLIMPORT ("GDI32", HFONT, CreateFontA, (int height, int width, int escapement, int orientation,
7273  int weight, DWORD italic, DWORD underline, DWORD strikeout,
7274  DWORD charSet, DWORD outputPrec, DWORD clipPrec,
7275  DWORD quality, DWORD pitchAndFamily, const char face[]));
7276 _TX_DLLIMPORT ("GDI32", int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc,
7277  LPARAM lParam, DWORD reserved));
7278 _TX_DLLIMPORT ("GDI32", COLORREF, SetPixel, (HDC dc, int x, int y, COLORREF color));
7279 _TX_DLLIMPORT ("GDI32", COLORREF, GetPixel, (HDC dc, int x, int y));
7280 _TX_DLLIMPORT ("GDI32", HPEN, CreatePen, (int penStyle, int width, COLORREF color));
7281 _TX_DLLIMPORT ("GDI32", HBRUSH, CreateSolidBrush, (COLORREF color));
7282 _TX_DLLIMPORT ("GDI32", bool, MoveToEx, (HDC dc, int x, int y, POINT* point));
7283 _TX_DLLIMPORT ("GDI32", bool, LineTo, (HDC dc, int x, int y));
7284 _TX_DLLIMPORT ("GDI32", bool, Polygon, (HDC dc, const POINT points[], int count));
7285 _TX_DLLIMPORT ("GDI32", bool, Polyline, (HDC dc, const POINT points[], int count));
7286 _TX_DLLIMPORT ("GDI32", bool, PolyBezier, (HDC dc, const POINT points[], int count));
7287 _TX_DLLIMPORT ("GDI32", bool, Rectangle, (HDC dc, int x0, int y0, int x1, int y1));
7288 _TX_DLLIMPORT ("GDI32", bool, RoundRect, (HDC dc, int x0, int y0, int x1, int y1, int sizeX, int sizeY));
7289 _TX_DLLIMPORT ("GDI32", bool, Ellipse, (HDC dc, int x0, int y0, int x1, int y1));
7290 _TX_DLLIMPORT ("GDI32", bool, Arc, (HDC dc, int x0, int y0, int x1, int y1,
7291  int xStart, int yStart, int xEnd, int yEnd));
7292 _TX_DLLIMPORT ("GDI32", bool, Pie, (HDC dc, int x0, int y0, int x1, int y1,
7293  int xStart, int yStart, int xEnd, int yEnd));
7294 _TX_DLLIMPORT ("GDI32", bool, Chord, (HDC dc, int x0, int y0, int x1, int y1,
7295  int xStart, int yStart, int xEnd, int yEnd));
7296 _TX_DLLIMPORT ("GDI32", bool, TextOutA, (HDC dc, int x, int y, const char string[], int length));
7297 _TX_DLLIMPORT ("GDI32", UINT, SetTextAlign, (HDC dc, unsigned mode));
7298 _TX_DLLIMPORT ("GDI32", bool, GetTextExtentPoint32A, (HDC dc, const char string[], int length, SIZE* size));
7299 _TX_DLLIMPORT ("GDI32", bool, ExtFloodFill, (HDC dc, int x, int y, COLORREF color, unsigned type));
7300 _TX_DLLIMPORT ("GDI32", bool, BitBlt, (HDC dest, int xDest, int yDest, int width, int height,
7301  HDC src, int xSrc, int ySrc, DWORD rOp));
7302 _TX_DLLIMPORT ("GDI32", bool, StretchBlt, (HDC dest, int xDest, int yDest, int width, int height,
7303  HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp));
7304 _TX_DLLIMPORT ("GDI32", bool, PlgBlt, (HDC dest, const POINT* parallelogram,
7305  HDC src, int xSrc, int ySrc, int width, int height,
7306  HBITMAP mask, int xMask, int yMask));
7307 _TX_DLLIMPORT ("GDI32", int, SetDIBitsToDevice, (HDC dc, int xDest, int yDest, DWORD width, DWORD height,
7308  int xSrc, int ySrc, unsigned startLine, unsigned numLines,
7309  const void* data, const BITMAPINFO* info, unsigned colorUse));
7310 _TX_DLLIMPORT ("GDI32", int, GetDIBits, (HDC hdc, HBITMAP hbmp, unsigned uStartScan, unsigned cScanLines,
7311  void* lpvBits, BITMAPINFO* lpbi, unsigned usage));
7312 _TX_DLLIMPORT ("GDI32", bool, PatBlt, (HDC dc, int x0, int y0, int width, int height, DWORD rOp));
7313 _TX_DLLIMPORT ("GDI32", int, SetROP2, (HDC dc, int mode));
7314 _TX_DLLIMPORT ("GDI32", int, SetStretchBltMode, (HDC dc, int mode));
7315 _TX_DLLIMPORT ("GDI32", DWORD, GdiSetBatchLimit, (DWORD limit));
7316 _TX_DLLIMPORT ("GDI32", HBITMAP, CreateDIBSection, (HDC dc, const BITMAPINFO* bmInfo, unsigned colorUsage, void **vBits,
7317  HANDLE section, DWORD offset));
7318 
7319 _TX_DLLIMPORT ("User32", int, DrawTextA, (HDC dc, const char text[], int length, RECT* rect, unsigned format));
7320 _TX_DLLIMPORT ("User32", HANDLE, LoadImageA, (HINSTANCE inst, const char name[], unsigned type,
7321  int sizex, int sizey, unsigned mode));
7322 _TX_DLLIMPORT_OPT ("User32", bool, IsHungAppWindow, (HWND wnd));
7323 _TX_DLLIMPORT_OPT ("User32", HWND, GhostWindowFromHungWindow, (HWND wnd));
7324 
7325 _TX_DLLIMPORT ("WinMM", bool, PlaySound, (const char sound[], HMODULE mod, DWORD mode));
7326 
7327 _TX_DLLIMPORT_OPT ("MSImg32", bool, TransparentBlt, (HDC dest, int destX, int destY, int destWidth, int destHeight,
7328  HDC src, int srcX, int srcY, int srcWidth, int srcHeight,
7329  unsigned transparentColor));
7330 _TX_DLLIMPORT_OPT ("MSImg32", bool, AlphaBlend, (HDC dest, int destX, int destY, int destWidth, int destHeight,
7331  HDC src, int srcX, int srcY, int srcWidth, int srcHeight,
7332  BLENDFUNCTION blending));
7333 
7334 _TX_DLLIMPORT ("Kernel32", void, ExitProcess, (unsigned retcode));
7335 _TX_DLLIMPORT ("Kernel32", bool, TerminateProcess, (HANDLE process, unsigned retcode));
7336 _TX_DLLIMPORT_OPT ("Kernel32", void, FatalExit, (int retcode));
7337 _TX_DLLIMPORT_OPT ("Kernel32", void, FatalAppExitA, (unsigned action, const char message[]));
7338 _TX_DLLIMPORT ("Kernel32", HWND, GetConsoleWindow, (void));
7339 _TX_DLLIMPORT_OPT ("Kernel32", bool, SetConsoleFont, (HANDLE con, DWORD fontIndex));
7340 _TX_DLLIMPORT_OPT ("Kernel32", DWORD, GetNumberOfConsoleFonts, (void));
7341 _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFont, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFO* curFont));
7342 _TX_DLLIMPORT_OPT ("Kernel32", bool, GetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont));
7343 _TX_DLLIMPORT_OPT ("Kernel32", bool, SetCurrentConsoleFontEx, (HANDLE con, bool maxWnd, CONSOLE_FONT_INFOEX* curFont));
7344 _TX_DLLIMPORT_OPT ("Kernel32", void, RtlCaptureContext, (CONTEXT* contextRecord));
7345 _TX_DLLIMPORT_OPT ("Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture, void** backTrace, DWORD* hash));
7346 _TX_DLLIMPORT_OPT ("Kernel32", void*, AddVectoredExceptionHandler, (unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler));
7347 _TX_DLLIMPORT_OPT ("Kernel32", unsigned, RemoveVectoredExceptionHandler,(void* handler));
7348 _TX_DLLIMPORT_OPT ("Kernel32", bool, GetModuleHandleEx, (DWORD flags, const char moduleName[], HMODULE* module));
7349 _TX_DLLIMPORT_OPT ("Kernel32", bool, IsWow64Process, (HANDLE process, int* isWow64Process));
7350 _TX_DLLIMPORT_OPT ("Kernel32", bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context));
7351 _TX_DLLIMPORT_OPT ("Kernel32", bool, SetThreadStackGuarantee, (unsigned long* stackSize));
7352 
7353 _TX_DLLIMPORT ("OLE32", HRESULT, CoInitialize, (void*));
7354 _TX_DLLIMPORT ("OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value));
7355 _TX_DLLIMPORT ("OLE32", void, CoUninitialize, (void));
7356 
7357 _TX_DLLIMPORT ("Shell32", HINSTANCE,ShellExecuteA, (HWND wnd, const char operation[], const char file[],
7358  const char parameters[], const char directory[], int showCmd));
7359 
7360 _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIA, (const char string[], const char search[]));
7361 _TX_DLLIMPORT ("ShlWAPI", char*, StrStrIW, (const wchar_t string[], const wchar_t search[]));
7362 
7363 _TX_DLLIMPORT_OPT ("NTDLL", char*, wine_get_version, (void));
7364 _TX_DLLIMPORT ("NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process, int infoClass,
7365  void* processInfo, unsigned long szProcessInfo, unsigned long* szReturnInfo));
7366 
7367 _TX_DLLIMPORT_CRT ("MSVCRT", void, exit, (int retcode));
7368 _TX_DLLIMPORT_CRT ("MSVCRT", void, _cexit, (void));
7369 _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _fpreset, (void));
7370 _TX_DLLIMPORT_CRT ("MSVCRT", unsigned, _controlfp, (unsigned control, unsigned mask));
7371 _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthread, (void (__cdecl* start_address) (void*), unsigned stack_size, void* arglist));
7372 _TX_DLLIMPORT_CRT ("MSVCRT", uintptr_t,_beginthreadex, (void* security, unsigned stack_size, unsigned (__stdcall* start_address) (void*),
7373  void *arglist, unsigned init_flag, unsigned* thread_addr));
7374 _TX_DLLIMPORT_CRT ("MSVCRT", char*, __unDName, (char* outStr, const char* mangledName, int outStrLen,
7375  void* (*mallocFunc) (size_t size), void (*freeFunc) (void *pointer),
7376  unsigned short flags));
7377 _TX_DLLIMPORT_CRT ("MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler));
7378 
7379 _TX_DLLIMPORT_OPT ("OpenGL32", HDC, wglGetCurrentDC, (void));
7380 _TX_DLLIMPORT_OPT ("OpenGL32", unsigned, glGetError, (void));
7381 _TX_DLLIMPORT_OPT ("Glu32", const char*, gluErrorString, (unsigned error));
7382 
7383 _TX_DLLIMPORT_OPT ("DbgHelp*", bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType,
7384  MINIDUMP_EXCEPTION_INFORMATION* exceptionParam,
7385  MINIDUMP_USER_STREAM_INFORMATION* userStreamParam,
7386  MINIDUMP_CALLBACK_INFORMATION* callbackParam));
7387 
7388 _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD, SymSetOptions, (DWORD options));
7389 _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess));
7390 _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol));
7391 _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line));
7392 _TX_DLLIMPORT_OPT ("DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr));
7393 _TX_DLLIMPORT_OPT ("DbgHelp*", bool, SymCleanup, (HANDLE process));
7394 _TX_DLLIMPORT_OPT ("DbgHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase));
7395 _TX_DLLIMPORT_OPT ("DbgHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord,
7396  PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc,
7397  PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc,
7398  PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc,
7399  PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc));
7400 namespace MinGW {
7401 _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD, SymSetOptions, (DWORD options));
7402 _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymInitialize, (HANDLE process, const char userSearchPath[], bool invadeProcess));
7403 _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol));
7404 _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line));
7405 _TX_DLLIMPORT_OPT ("MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr));
7406 _TX_DLLIMPORT_OPT ("MgwHelp*", bool, SymCleanup, (HANDLE process));
7407 _TX_DLLIMPORT_OPT ("MgwHelp*", void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase));
7408 _TX_DLLIMPORT_OPT ("MgwHelp*", bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame, void* ctxRecord,
7409  PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc,
7410  PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc,
7411  PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc,
7412  PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc));
7413 } // namespace MinGW
7414 
7415 } // namespace Win32
7416 
7417 //}
7418 //-----------------------------------------------------------------------------------------------------------------
7419 
7421 //}
7422 //=================================================================================================================
7423 
7424 //=================================================================================================================
7425 //{ Internal function prototypes, macros and constants
7426 // @name Прототипы внутренних функций, макросы и константы//================================================================================================================= //! @{ const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна _TX_IDM_CONSOLE = 40001, _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas //----------------------------------------------------------------------------------------------------------------- int _txInitialize(); void _txCleanup(); HWND _txCanvas_CreateWindow (const SIZE* size); inline bool _txCanvas_OK() tx_nodiscard; bool _txCanvas_OnCREATE (HWND wnd); bool _txCanvas_OnDESTROY (HWND wnd); bool _txCanvas_OnCLOSE (HWND); bool _txCanvas_OnPAINT (HWND wnd); bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info); bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info); bool _txCanvas_OnTIMER (HWND wnd, WPARAM id); bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords); bool _txCanvas_OnMOUSELEAVE (HWND wnd); bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar); bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd); bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd); unsigned WINAPI _txCanvas_ThreadProc (void* data); LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); int _txCanvas_SetRefreshLock (int count); HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL, RGBQUAD** pixels = NULL) tx_nodiscard; bool _txBuffer_Delete (HDC* dc); bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC()); HWND _txConsole_Attach(); bool _txConsole_OK() tx_nodiscard; bool _txConsole_Detach (bool activate); bool _txConsole_Draw (HDC dc); bool _txConsole_SetUnicodeFont(); const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra); HWND txCreateExtraWindow (CREATESTRUCT createData); HICON _txCreateTXIcon (int size) tx_nodiscard; int _txSetFinishedText (HWND wnd); void _txPauseBeforeTermination (HWND canvas); int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard; int _txGetInput(); LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar); const char* _txPlayVideo_FindVLC() tx_nodiscard; bool _txCreateShortcut (const char shortcutName[], const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL, const char description[] = NULL, int cmdShow = SW_SHOWNORMAL, const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0, COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD)); void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) tx_nodiscard; void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]); const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0, const char msg[] = NULL, ...) tx_printfy (5); const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args); void _txOnTerminate(); void _txOnUnexpected(); void _txOnPureCall(); void _txOnNewHandlerAnsi(); int _txOnNewHandler (size_t size); void _txOnSignal (int signal = 0, int fpe = 0); BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type); void _txOnSecurityError (int code, void*); void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code); int _txOnMatherr (_exception* except); void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file, unsigned line, uintptr_t); int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line); int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5); int _txOnErrorReport (int type, const char* text, int* ret); int tx_glGetError (int setError = INT_MIN); void _txOnCExit(); void _txOnExit (int retcode); void _txOnFatalExit (int retcode); void _txOnExitProcess (unsigned retcode); void _txOnFatalAppExitA (unsigned action, const char message[]); bool _txOnTerminateProcess (HANDLE process, unsigned retcode); LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter); void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc); long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc); long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]); intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]); intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type); intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0, unsigned params = 0, const ULONG_PTR info[] = NULL); void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?", bool readSource = true); const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash, CONTEXT* context = NULL, HANDLE thread = GetCurrentThread()); const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false); const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2); bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL, Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL, const char** source = NULL, int context = 2); intptr_t _txReadSource (char buf[], intptr_t size, const char file[], int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN); bool _txCreateMiniDump (EXCEPTION_POINTERS* exc); uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL, int useHotPatching = false, HMODULE module = NULL, bool debug = false); bool _txInDll() tx_nodiscard; PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard; bool _txKillProcess (DWORD pid); bool _txIsBadReadPtr (const void* address); bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true); bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid()); IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard; const char* _txAppInfo() tx_nodiscard; intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3); intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg); void txReopenStdio(); #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info); #endif #if defined (__CYGWIN__) int _getch(); int _putch (int ch); int _kbhit() tx_nodiscard; #endif //----------------------------------------------------------------------------------------------------------------- // There are macros for __FILE__ and __LINE__ to work properly. #if !defined (NDEBUG) #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \ (assert (cond), true) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \ (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \ "или уже уничтожен, или не загрузилась картинка.", #dc), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \ _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) ) #else #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \ (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) ) #define _TX_TXWINDOW_FAILED() ( !txOK() && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \ (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) ) #endif //----------------------------------------------------------------------------------------------------------------- // Take action in debug configuration only. // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'( #if !defined (NDEBUG) #define _TX_ON_DEBUG( code ) { code; } #else #define _TX_ON_DEBUG( code ) ; #endif //----------------------------------------------------------------------------------------------------------------- // Invokes an error without location information. "$$" restores TX-related call location context #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__) //----------------------------------------------------------------------------------------------------------------- // Safe call of a function via its pointer #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 ) #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 ) //----------------------------------------------------------------------------------------------------------------- // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x. #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \ !(cond) && GetTickCount() < _t; \ Sleep (_txWindowUpdateInterval)) ; \ if (!(cond)) \ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); } //----------------------------------------------------------------------------------------------------------------- // Detouring in case of SEH mechanism #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 ) #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; } //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal global data //! @name Внутренние глобальные данные // // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :) // // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же. // Здесь это сделано только в образовательных целях. // // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс. //================================================================================================================= //! @{ int _txInitialized = _TX_NOINIT || _txInitialize(); volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main() volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT() RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0] HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP), // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock() UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question. bool _txConsole = false; // Only first TXLib module in app can own the console bool _txMain = false; // First TXLib wnd opened (closing it terminates program) bool _txIsDll = false; // TXLib module is in DLL volatile bool _txRunning = false; // main() is still running volatile bool _txExit = false; // exit() is active volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE() volatile unsigned _txMouseButtons = 0; volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook(). _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro volatile int _txErrors = 0; // TX_ERROR calls sequental number int _txOGLError = 0; // Last OpenGL error when using tx_glGetError() volatile long _txSENumber = 0; // SEH exceptions sequental number volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess, (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA, (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp, (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier, (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler, (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace, (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize, (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions, (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64, (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr, (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup, (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64, (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64, (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64, (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext }; volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib engine init/check/cleanup //! @name Инициализация/проверка/завершение TXLib //================================================================================================================= //! @{ //----------------------------------------------------------------------------------------------------------------- //{ Early initialization //----------------------------------------------------------------------------------------------------------------- int _txInitialize() { if (_txInitialized) return 1; _txInitialized = 1; #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585 #endif #if defined (_TX_ALLOW_TRACE) _txLocLvlSet (1); #endif _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" " "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n"); OutputDebugString ("\n")); _txMainThreadId = GetCurrentThreadId(); _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId); $3 _txIsDll = _txInDll(); $ if (!_txIsDll) { $ _txConsole = ! FindAtom ("_txConsole"); $ (void) AddAtom ("_txConsole"); } $ if (_txConsole) { $ _txCheckSourceCP (_TX_CODEPAGE, true); $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ _txOnSignal(); $ if (!*_txLogName) {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); } $ if (!_txIsDll) { $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler)); $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter); } $ ::std::set_terminate (_txOnTerminate); $ ::std::set_new_handler (_txOnNewHandlerAnsi); $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected)); #if defined (_CLANG_VER) && !defined (_MSC_VER) $ ::std::__libcpp_debug_function = _txLibCppDebugFunction; #endif $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true); $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); #if defined (_MSC_VER) $ _set_printf_count_output (1); $ _set_new_handler (_txOnNewHandler); $ _set_new_mode (1); #if !defined (_CLANG_VER) $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF); $ _CrtSetAllocHook (_txOnAllocHook); $ unsigned mode = _CRTDBG_MODE_FILE; $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0; $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode); $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW); $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); #endif $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG); $ _set_abort_behavior (0, _CALL_REPORTFAULT); $ _RTC_SetErrorFunc (_txOnRTCFailure); $ _set_purecall_handler (_txOnPureCall); $ _set_invalid_parameter_handler (_txOnInvalidParam); #endif #if defined (__STDC_LIB_EXT1__) $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi); #endif #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386) $ __setusermatherr (_txOnMatherr); #endif #if !defined (__CYGWIN__) $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR); #endif $ HWND console = _txConsole_Attach(); $ SetWindowTextA (console, txGetModuleFileName (false)); } $ InitializeCriticalSection (&_txCanvas_LockBackBuf); $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL"); $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL"); $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true); $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL"); $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit); $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit); $ atexit (_txCleanup); $ if (_txConsole) { $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ tx_fpreset(); $ srand ((unsigned) time (NULL)); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif } $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted; $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted; $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted; $ Win32::DeleteDC (dc) asserted; $ return 1; } //----------------------------------------------------------------------------------------------------------------- bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/) { $3 const char* sCodePage = NULL; $ int codePage = 0; $ switch (((unsigned const char*) "А") [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
7427 //=================================================================================================================
7429 
7430 const int _TX_IDM_ABOUT = 40000, // Идентификаторы системного меню окна
7431  _TX_IDM_CONSOLE = 40001,
7432  _TX_WM_CREATEWND = 0x7FF0, // Сообщения для создания/уничтожения
7433  _TX_WM_DESTROYWND = 0x7FF1; // окон в потоке Canvas
7434 
7435 //-----------------------------------------------------------------------------------------------------------------
7436 
7437 int _txInitialize();
7438 void _txCleanup();
7439 
7440 HWND _txCanvas_CreateWindow (const SIZE* size);
7441 inline bool _txCanvas_OK() tx_nodiscard;
7442 
7443 bool _txCanvas_OnCREATE (HWND wnd);
7444 bool _txCanvas_OnDESTROY (HWND wnd);
7445 bool _txCanvas_OnCLOSE (HWND);
7446 bool _txCanvas_OnPAINT (HWND wnd);
7447 bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info);
7448 bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info);
7449 bool _txCanvas_OnTIMER (HWND wnd, WPARAM id);
7450 bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords);
7451 bool _txCanvas_OnMOUSELEAVE (HWND wnd);
7452 bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar);
7453 bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar);
7454 bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd);
7455 bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd);
7456 
7457 unsigned WINAPI _txCanvas_ThreadProc (void* data);
7458 LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar);
7459 
7460 int _txCanvas_SetRefreshLock (int count);
7461 
7462 HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL,
7463  RGBQUAD** pixels = NULL) tx_nodiscard;
7464 bool _txBuffer_Delete (HDC* dc);
7465 bool _txBuffer_Select (HGDIOBJ obj, HDC dc = txDC());
7466 
7467 HWND _txConsole_Attach();
7468 bool _txConsole_OK() tx_nodiscard;
7469 bool _txConsole_Detach (bool activate);
7470 bool _txConsole_Draw (HDC dc);
7471 bool _txConsole_SetUnicodeFont();
7472 
7473 const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra);
7474 HWND txCreateExtraWindow (CREATESTRUCT createData);
7475 HICON _txCreateTXIcon (int size) tx_nodiscard;
7476 int _txSetFinishedText (HWND wnd);
7477 void _txPauseBeforeTermination (HWND canvas);
7478 int _txIsParentWaitable (DWORD* parentPID = NULL) tx_nodiscard;
7479 int _txGetInput();
7480 
7481 LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar);
7482 const char* _txPlayVideo_FindVLC() tx_nodiscard;
7483 
7484 bool _txCreateShortcut (const char shortcutName[],
7485  const char fileToLink[], const char args[] = NULL, const char workDir[] = NULL,
7486  const char description[] = NULL, int cmdShow = SW_SHOWNORMAL,
7487  const char iconFile[] = NULL, int iconIndex = 0, int fontSize = 0,
7488  COORD bufSize = ZERO (COORD), COORD wndSize = ZERO (COORD), COORD wndOrg = ZERO (COORD));
7489 
7490 void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle,
7491  WORD controls, short x, short y, short cx, short cy,
7492  const char caption[], const char font[], WORD fontsize,
7493  const char menu[]) tx_nodiscard;
7494 
7495 void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle,
7496  short x, short y, short cx, short cy,
7497  WORD id, const char wclass[], const char caption[]);
7498 
7499 const char* _txError (const char file[] = NULL, int line = 0, const char func[] = NULL, unsigned color = 0,
7500  const char msg[] = NULL, ...) tx_printfy (5);
7501 const char* _txProcessError (const char file[], int line, const char func[], unsigned color,
7502  const char msg[], va_list args);
7503 void _txOnTerminate();
7504 void _txOnUnexpected();
7505 void _txOnPureCall();
7506 void _txOnNewHandlerAnsi();
7507 int _txOnNewHandler (size_t size);
7508 void _txOnSignal (int signal = 0, int fpe = 0);
7509 BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type);
7510 void _txOnSecurityError (int code, void*);
7511 void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code);
7512 int _txOnMatherr (_exception* except);
7513 void _txOnInvalidParam (const wchar_t* expr, const wchar_t* func, const wchar_t* file,
7514  unsigned line, uintptr_t);
7515 int _txOnAllocHook (int type, void* data, size_t size, int use, long request,
7516  const unsigned char* file, int line);
7517 int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) tx_printfy (5);
7518 int _txOnErrorReport (int type, const char* text, int* ret);
7519 int tx_glGetError (int setError = INT_MIN);
7520 
7521 void _txOnCExit();
7522 void _txOnExit (int retcode);
7523 void _txOnFatalExit (int retcode);
7524 void _txOnExitProcess (unsigned retcode);
7525 void _txOnFatalAppExitA (unsigned action, const char message[]);
7526 bool _txOnTerminateProcess (HANDLE process, unsigned retcode);
7527 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
7528  _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter);
7529 void _txWatchdogTerminator (void* timeout); // Only Arnold-type series are supported, not T1000
7530 
7531 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc);
7532 long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc);
7533 long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]);
7534 intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]);
7535 intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type);
7536 intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code = 0,
7537  unsigned params = 0, const ULONG_PTR info[] = NULL);
7538 
7539 void _txStackBackTrace (const char file[] = "?", int line = 0, const char func[] = "?",
7540  bool readSource = true);
7541 const char* _txCaptureStackBackTrace (int framesToSkip = 0, bool readSource = true,
7542  CONTEXT* context = NULL, HANDLE thread = GetCurrentThread());
7543 int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* backTraceHash,
7544  CONTEXT* context = NULL, HANDLE thread = GetCurrentThread());
7545 const char* _txCaptureStackBackTraceTX (int framesToSkip = 0, bool readSource = false);
7546 
7547 const char* _txSymPrintFromAddr (void* addr = NULL, const char format[] = NULL, ...) tx_printfy (2);
7548 bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol = NULL,
7549  Win32::IMAGEHLP_LINE64** line = NULL, const char** module = NULL,
7550  const char** source = NULL, int context = 2);
7551 intptr_t _txReadSource (char buf[], intptr_t size, const char file[],
7552  int linStart = 0, int linEnd = INT_MIN, int linMark = INT_MIN);
7553 bool _txCreateMiniDump (EXCEPTION_POINTERS* exc);
7554 
7555 uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] = NULL,
7556  int useHotPatching = false, HMODULE module = NULL, bool debug = false);
7557 bool _txInDll() tx_nodiscard;
7558 PROCESSENTRY32* _txFindProcess (unsigned pid = GetCurrentProcessId()) tx_nodiscard;
7559 bool _txKillProcess (DWORD pid);
7560 bool _txIsBadReadPtr (const void* address);
7561 bool _txCheckSourceCP (int needCP = _TX_CODEPAGE, bool verbose = true);
7562 bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid = _getpid());
7563 IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL)) tx_nodiscard;
7564 const char* _txAppInfo() tx_nodiscard;
7565 
7566 intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) tx_printfy (3);
7567 intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg);
7568 void txReopenStdio();
7569 
7570 #if defined (_CLANG_VER) && !defined (_MSC_VER)
7571 void _txLibCppDebugFunction (std::__libcpp_debug_info const& info);
7572 #endif
7573 
7574 #if defined (__CYGWIN__)
7575 
7576 int _getch();
7577 int _putch (int ch);
7578 int _kbhit() tx_nodiscard;
7579 
7580 #endif
7581 
7582 //-----------------------------------------------------------------------------------------------------------------
7583 // There are macros for __FILE__ and __LINE__ to work properly.
7584 
7585 #if !defined (NDEBUG)
7586 
7587  #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \
7588  (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \
7589  (assert (cond), true) )
7590 
7591  #define _TX_TXWINDOW_FAILED() ( !txOK() && \
7592  (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \
7593  (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) )
7594 
7595  #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \
7596  (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \
7597  (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \
7598  "или уже уничтожен, или не загрузилась картинка.", #dc), 1) )
7599  #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \
7600  _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) )
7601 
7602 #else
7603  #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \
7604  (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) )
7605 
7606  #define _TX_TXWINDOW_FAILED() ( !txOK() && \
7607  (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) )
7608 
7609  #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \
7610  (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) )
7611 
7612  #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \
7613  (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) )
7614 #endif
7615 
7616 //-----------------------------------------------------------------------------------------------------------------
7617 // Take action in debug configuration only.
7618 // Definition ({ expr; }) would be better, but MSVC rejects it. So sad. :'(
7619 
7620 #if !defined (NDEBUG)
7621  #define _TX_ON_DEBUG( code ) { code; }
7622 #else
7623  #define _TX_ON_DEBUG( code ) ;
7624 #endif
7625 
7626 //-----------------------------------------------------------------------------------------------------------------
7627 // Invokes an error without location information. "$$" restores TX-related call location context
7628 
7629 #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__)
7630 
7631 //-----------------------------------------------------------------------------------------------------------------
7632 // Safe call of a function via its pointer
7633 
7634 #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 )
7635 #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 )
7636 
7637 //-----------------------------------------------------------------------------------------------------------------
7638 // This is a macro because cond is an expression and is not always a function. Lack of lambdas in pre-C++0x.
7639 
7640 #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \
7641  !(cond) && GetTickCount() < _t; \
7642  Sleep (_txWindowUpdateInterval)) ; \
7643  if (!(cond)) \
7644  _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); }
7645 //-----------------------------------------------------------------------------------------------------------------
7646 // Detouring in case of SEH mechanism
7647 
7648 #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 )
7649 
7650 #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; }
7651 
7653 //}
7654 //=================================================================================================================
7655 
7656 //=================================================================================================================
7657 //{ Internal global data
7659 //
7660 // Данные не упакованы в структуру или класс, для того, чтобы это сделали Вы сами :)
7661 //
7662 // Если вы пишете свою библиотеку и используете TXLib.h как пример, не следуйте ему и не делайте так же.
7663 // Здесь это сделано только в образовательных целях.
7664 //
7665 // Будьте практичнее, сделайте структуру и глобальную функцию для доступа к ней, или класс.
7666 //=================================================================================================================
7668 
7669 int _txInitialized = _TX_NOINIT || _txInitialize();
7670 
7671 volatile const unsigned _txCanaryFirst = 0x776F656D; // A very system value
7672 
7673 volatile unsigned _txMainThreadId = 0; // ID потока, где выполняется main()
7674 volatile HANDLE _txMainThread = NULL; // Дексриптор этого потока
7675 
7676 volatile unsigned _txCanvas_ThreadId = 0; // ID потока, владеющего окном холста TXLib
7677 volatile HANDLE _txCanvas_Thread = NULL; // Дексриптор этого потока
7678 volatile HWND _txCanvas_Window = NULL; // Дескриптор окна холста TXLib
7679 
7680 HDC _txCanvas_BackBuf[2] = {NULL, // [0] Main TXLib in-memory DC, where user's pictures lies
7681  NULL}; // [1] Image ready for auto-refresh, see txCanvas_OnPAINT()
7682 
7683 RGBQUAD* _txCanvas_Pixels = NULL; // Memory buffer of _txCanvas_BackBuf[0]
7684 
7685 HBITMAP _txStockBitmap = NULL; // Equivalent of GetStockObject (BITMAP),
7686  // see https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313
7687 
7688 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1}; // Prevent simultaneous access to back buffer, see txLock()
7689 
7690 UINT_PTR _txCanvas_RefreshTimer = 1; // Timer ID to redraw TXLib window
7691 volatile int _txCanvas_RefreshLock = 0; // Blocks auto on-timer canvas update, see txBegin/txEnd
7692 
7693 ::std::vector<HDC>* _txCanvas_UserDCs = NULL; // List of DCs allocated, for auto-free
7694 
7695 volatile bool _txConsole_IsBlinking = true; // To blink or not to blink, that is the question.
7696 
7697 bool _txConsole = false; // Only first TXLib module in app can own the console
7698 bool _txMain = false; // First TXLib wnd opened (closing it terminates program)
7699 bool _txIsDll = false; // TXLib module is in DLL
7700 volatile bool _txRunning = false; // main() is still running
7701 volatile bool _txExit = false; // exit() is active
7702 
7703 volatile POINT _txMousePos = {-1,-1}; // Ask Captn Obviouos about it. See txCanvas_OnMOUSE()
7704 volatile unsigned _txMouseButtons = 0;
7705 
7706 volatile WNDPROC _txAltWndProc = NULL; // Альтернативная оконная функция. См. txSetWindowsHook().
7707 
7708 _tx_thread _txLoc _txLoc::Cur = {}; // Execution point tracking and trace state, see "$" macro
7709 
7710 volatile int _txErrors = 0; // TX_ERROR calls sequental number
7711 int _txOGLError = 0; // Last OpenGL error when using tx_glGetError()
7712 volatile long _txSENumber = 0; // SEH exceptions sequental number
7713 volatile long _txSEFatalNumber = 0; // SEH fatal exceptions sequental number
7714 char _txDumpSE [_TX_BUFSIZE] = ""; // SEH dump data area
7715 char _txTraceSE[_TX_HUGEBUFSIZE] = ""; // Stack trace data area
7716 
7717 LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL; // Previous UnhandledExceptionFilter
7718 
7719 jmp_buf _txDumpExceptionObjJmp = {}; // Hook for _txDumpExceptionObj
7720 
7721 const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess,
7722  (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA,
7723  (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp,
7724  (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier,
7725  (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler,
7726  (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace,
7727  (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize,
7728  (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions,
7729  (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64,
7730  (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr,
7731  (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup,
7732  (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64,
7733  (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64,
7734  (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64,
7735  (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext };
7736 
7737 volatile const unsigned _txCanaryLast = 0x5E2E2E5E; // Another very system value
7738 
7740 //}
7741 //=================================================================================================================
7742 
7743 //=================================================================================================================
7744 //{ TXLib engine init/check/cleanup
7746 //=================================================================================================================
7748 
7749 //-----------------------------------------------------------------------------------------------------------------
7750 //{ Early initialization
7751 //-----------------------------------------------------------------------------------------------------------------
7752 
7753 int _txInitialize()
7754  {
7755  if (_txInitialized) return 1;
7756  _txInitialized = 1;
7757 
7758  #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx
7759  _CrtSetBreakAlloc (_TX_ALLOC_BREAK); // and http://support.microsoft.com/ru-ru/kb/151585
7760  #endif
7761 
7762  #if defined (_TX_ALLOW_TRACE)
7763  _txLocLvlSet (1);
7764  #endif
7765 
7766  _TX_ON_DEBUG (OutputDebugString ("\n");
7767  OutputDebugString (_TX_VERSION " - The Dumb Artist Library, " _TX_AUTHOR ": \"" __FILE__ "\" "
7768  "compiled " __DATE__ " " __TIME__ ", " _TX_BUILDMODE " mode, module: " _TX_MODULE "\n");
7769  OutputDebugString ("\n"));
7770 
7771  _txMainThreadId = GetCurrentThreadId();
7772  _txMainThread = OpenThread (THREAD_ALL_ACCESS, false, _txMainThreadId);
7773 
7774 $3 _txIsDll = _txInDll();
7775 
7776 $ if (!_txIsDll)
7777  {
7778 $ _txConsole = ! FindAtom ("_txConsole");
7779 $ (void) AddAtom ("_txConsole");
7780  }
7781 
7782 $ if (_txConsole)
7783  {
7784 $ _txCheckSourceCP (_TX_CODEPAGE, true);
7785 
7786 $ unsigned long stackSize = _TX_STACKSIZE;
7787 $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize));
7788 
7789 $ _txOnSignal();
7790 
7791 $ if (!*_txLogName)
7792  {$ _tx_snprintf_s (_txLogName, sizeof (_txLogName) - 1, "%s.log", txGetModuleFileName()); }
7793 
7794 $ if (!_txIsDll)
7795  {
7796 $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler));
7797 $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter);
7798  }
7799 
7800 $ ::std::set_terminate (_txOnTerminate);
7801 $ ::std::set_new_handler (_txOnNewHandlerAnsi);
7802 $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected));
7803 
7804  #if defined (_CLANG_VER) && !defined (_MSC_VER)
7805 $ ::std::__libcpp_debug_function = _txLibCppDebugFunction;
7806  #endif
7807 
7808 $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent, true);
7809 
7810 $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
7811 
7812  #if defined (_MSC_VER)
7813 
7814 $ _set_printf_count_output (1);
7815 
7816 $ _set_new_handler (_txOnNewHandler);
7817 $ _set_new_mode (1);
7818 
7819  #if !defined (_CLANG_VER)
7820 
7821 $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF);
7822 $ _CrtSetAllocHook (_txOnAllocHook);
7823 
7824 $ unsigned mode = _CRTDBG_MODE_FILE;
7825 $ if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0;
7826 
7827 $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode);
7828 $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW);
7829 $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW);
7830 $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR);
7831 $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR);
7832 $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR);
7833 
7834  #endif
7835 
7836 $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG);
7837 $ _set_abort_behavior (0, _CALL_REPORTFAULT);
7838 
7839 $ _RTC_SetErrorFunc (_txOnRTCFailure);
7840 $ _set_purecall_handler (_txOnPureCall);
7841 $ _set_invalid_parameter_handler (_txOnInvalidParam);
7842 
7843  #endif
7844 
7845  #if defined (__STDC_LIB_EXT1__)
7846 $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi);
7847  #endif
7848 
7849  #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386)
7850 $ __setusermatherr (_txOnMatherr);
7851  #endif
7852 
7853  #if !defined (__CYGWIN__)
7854 $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR);
7855  #endif
7856 
7857 $ HWND console = _txConsole_Attach();
7858 $ SetWindowTextA (console, txGetModuleFileName (false));
7859  }
7860 
7861 $ InitializeCriticalSection (&_txCanvas_LockBackBuf);
7862 
7863 $ _txSetProcAddress ("ExitProcess", (uintptr_t) _txOnExitProcess, "KERNEL32.DLL");
7864 $ _txSetProcAddress ("TerminateProcess", (uintptr_t) _txOnTerminateProcess, "KERNEL32.DLL");
7865 $ _txSetProcAddress ("FatalExit", (uintptr_t) _txOnFatalExit, "KERNEL32.DLL");
7866 $ _txSetProcAddress ("FatalAppExitA", (uintptr_t) _txOnFatalAppExitA, "KERNEL32.DLL");
7867 $ _txSetProcAddress ("UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter, "KERNEL32.DLL", true);
7868 $ _txSetProcAddress ("SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter, "KERNEL32.DLL");
7869 $ _txSetProcAddress ("exit", (uintptr_t) _txOnExit);
7870 $ _txSetProcAddress ("_cexit", (uintptr_t) _txOnCExit);
7871 
7872 $ atexit (_txCleanup);
7873 
7874 $ if (_txConsole)
7875  {
7876 $ txSetConsoleAttr (FOREGROUND_LIGHTGRAY);
7877 
7878 $ tx_fpreset();
7879 
7880 $ srand ((unsigned) time (NULL));
7881 
7882 $ SetLastError (0);
7883 $ errno = 0;
7884 
7885  #if !defined (__CYGWIN__)
7886 $ _doserrno = 0;
7887  #endif
7888  }
7889 
7890 $ HDC dc = Win32::CreateCompatibleDC (NULL); dc asserted;
7891 $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap asserted;
7892 $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap)) asserted;
7893 $ Win32::DeleteDC (dc) asserted;
7894 
7895 $ return 1;
7896  }
7897 
7898 //-----------------------------------------------------------------------------------------------------------------
7899 
7900 bool _txCheckSourceCP (int needCP /*= _TX_CODEPAGE*/, bool verbose /*= true*/)
7901  {
7902 $3 const char* sCodePage = NULL;
7903 $ int codePage = 0;
7904 
7905 $ switch (((unsigned const char*) "А) [0]) { case 192: {$ codePage = 1251; sCodePage = "1251."; break; } case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; } case 128: {$ codePage = 866; sCodePage = "866."; break; } case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; } default: {$ codePage = -1; sCodePage = "(Unknown)"; break; } } $ if (codePage != needCP && verbose) { $ *_txTraceSE = ' '; // No stack trace please $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n" "This is NOT an error of TXLib itself. Please note:\n\n" "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your " "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO " "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. " "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n" "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n" "You can continue, but Russian messages and symbols may appear unreadable.", sCodePage, needCP, needCP); } $ return (codePage == needCP); } //----------------------------------------------------------------------------------------------------------------- _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/) { if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL; if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL; static char dllPaths [2][MAX_PATH] = {"", ""}; if (!*dllPaths[0]) { const char dllDir[] = "\\Windows\\"; // dllPaths[0] is relative to the TX Setup directory stored in the Registry char* path = dllPaths[0]; txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH); strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); // dllPaths[1] is relative to TXib.h file used in compilation path = dllPaths[1]; if (strchr (__FILE__, ':')) { strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1); } else { GetCurrentDirectory (MAX_PATH, path); strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1); } if (char* dir = strrchr (path, '\\')) *dir = 0; strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1); } char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = ""; const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL); if (arch) { assert (arch >= dllFileName); strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName)); strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName)); strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3); strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch)); } else if (dllFileName) { strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1); } HMODULE dll = GetModuleHandle (dllFileName); if (!dll) dll = GetModuleHandle (dllArch); if (!dll) dll = GetModuleHandle (dllName); for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++) { char path [MAX_PATH] = ""; strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i])); size_t len = strlen (path); strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch)); if (!dll) dll = LoadLibrary (path); strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName)); if (!dll) dll = LoadLibrary (path); } if (!dll) dll = LoadLibrary (dllArch); if (!dll) dll = LoadLibrary (dllName); if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".", dllName, (arch? "\" / \"" : ""), dllArch); if (!dll) return NULL; _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName); if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".", funcName, dllName, (arch? "\" / \"" : ""), dllArch); return addr; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013 #pragma warning (push) #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку) [0])
7906  {
7907  case 192: {$ codePage = 1251; sCodePage = "1251."; break; }
7908  case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; }
7909  case 128: {$ codePage = 866; sCodePage = "866."; break; }
7910  case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; }
7911  default: {$ codePage = -1; sCodePage = "(Unknown)"; break; }
7912  }
7913 
7914 $ if (codePage != needCP && verbose)
7915  {
7916 $ *_txTraceSE = ' '; // No stack trace please
7917 
7918 $ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n"
7919  "This is NOT an error of TXLib itself. Please note:\n\n"
7920  "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your "
7921  "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO "
7922  "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. "
7923  "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n"
7924  "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n"
7925  "You can continue, but Russian messages and symbols may appear unreadable.",
7926  sCodePage, needCP, needCP);
7927  }
7928 
7929 $ return (codePage == needCP);
7930  }
7931 
7932 //-----------------------------------------------------------------------------------------------------------------
7933 
7934 _tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/)
7935  {
7936  if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL;
7937  if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL;
7938 
7939  static char dllPaths [2][MAX_PATH] = {"", ""};
7940 
7941  if (!*dllPaths[0])
7942  {
7943  const char dllDir[] = "\\Windows\\";
7944 
7945  // dllPaths[0] is relative to the TX Setup directory stored in the Registry
7946 
7947  char* path = dllPaths[0];
7948 
7949  txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH);
7950  strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1);
7951 
7952  // dllPaths[1] is relative to TXib.h file used in compilation
7953 
7954  path = dllPaths[1];
7955 
7956  if (strchr (__FILE__, ':'))
7957  {
7958  strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1);
7959  }
7960  else
7961  {
7962  GetCurrentDirectory (MAX_PATH, path);
7963  strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1);
7964  }
7965 
7966  if (char* dir = strrchr (path, '\\')) *dir = 0;
7967 
7968  strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1);
7969  }
7970 
7971  char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = "";
7972  const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL);
7973 
7974  if (arch)
7975  {
7976  assert (arch >= dllFileName);
7977 
7978  strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName));
7979  strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName));
7980 
7981  strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName));
7982  strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3);
7983  strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch));
7984  }
7985  else if (dllFileName)
7986  {
7987  strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1);
7988  }
7989 
7990  HMODULE dll = GetModuleHandle (dllFileName);
7991 
7992  if (!dll) dll = GetModuleHandle (dllArch);
7993  if (!dll) dll = GetModuleHandle (dllName);
7994 
7995  for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++)
7996  {
7997  char path [MAX_PATH] = "";
7998  strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i]));
7999  size_t len = strlen (path);
8000 
8001  strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch));
8002  if (!dll) dll = LoadLibrary (path);
8003 
8004  strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName));
8005  if (!dll) dll = LoadLibrary (path);
8006  }
8007 
8008  if (!dll) dll = LoadLibrary (dllArch);
8009  if (!dll) dll = LoadLibrary (dllName);
8010 
8011  if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".",
8012  dllName, (arch? "\" / \"" : ""), dllArch);
8013  if (!dll) return NULL;
8014 
8015  _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName);
8016 
8017  if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".",
8018  funcName, dllName, (arch? "\" / \"" : ""), dllArch);
8019  return addr;
8020  }
8021 
8022 //-----------------------------------------------------------------------------------------------------------------
8023 
8024 #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013
8025  #pragma warning (push)
8026  #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции#endif int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue) { if (_TX_ARGUMENT_FAILED (keyName)) return 0; HKEY hive = NULL; #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \ _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 ) if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE; else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER; else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT; else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS; else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG; else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; } #undef EQU_ keyName = strchr (keyName, '\\') + 1; HKEY key = NULL; DWORD size = 0; bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS; if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS; if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS; if (key) ok &= !!RegCloseKey (key); return size; } #if defined (_MSC_VER) && (_MSC_VER == 1800) #pragma warning (pop) #endif //} //----------------------------------------------------------------------------------------------------------------- HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/) { $1 if (!_txInitialized) _txInitialized = _txInitialize(); $ if (HWND wnd = txWindow()) { $ SetLastErrorEx (ERROR_INVALID_DATA, 0); $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!")); $ return wnd; } $ if (!_txIsDll) { $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe $ (void) AddAtom ("_txMain"); } $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; } $ _txRunning = false; // Store the size $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) }; $ if (centered) { size.cx *= -1; size.cy *= -1; } // In Thread, where REAL creation lies... $ unsigned id = 0; $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id); $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL; $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT); $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL; $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL; $ HWND console = Win32::GetConsoleWindow(); $ DWORD proc = 0; $ GetWindowThreadProcessId (console, &proc); $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable())) {$ ShowWindow (console, _txConsoleMode); } $ HMENU menu = GetSystemMenu (txWindow(), false); if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); } $ Win32::GdiSetBatchLimit (1); $ SetLastError (0); $ errno = 0; #if !defined (__CYGWIN__) $ _doserrno = 0; #endif $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- HWND txCreateExtraWindow (CREATESTRUCT createData) { $1 if (_TX_TXWINDOW_FAILED()) return NULL; $ volatile HWND wnd = NULL; $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd; $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted; $ _txWaitFor (wnd, 5*_TX_TIMEOUT); $ return wnd; } //----------------------------------------------------------------------------------------------------------------- bool txSetDefaults (HDC dc /*= txDC()*/) { $1 if (dc == txDC()) txUpdateWindow (false); $ txAutoLock _lock; $ RECT r = {}; $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted; $ SIZE szCon = { r.right - r.left, r.bottom - r.top }; $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}}; $ GetConsoleScreenBufferInfo (out, &con); $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1), (short) (con.srWindow.Bottom - con.srWindow.Top + 1) }; //{ Set defaults for graphics layer $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted; $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted; $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont), dc) asserted; $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted; $ Win32::SetBkMode (dc, TRANSPARENT) asserted; $ Win32::SetROP2 (dc, R2_COPYPEN) asserted; $ Win32::SetStretchBltMode (dc, HALFTONE) asserted; //} $ if (dc != txDC()) {$ return true; } //{ Set defaults for console layer $ POINT szCanvas = txGetExtent (dc); $ HGDIOBJ font = txFontExist (_txConsoleFont)? Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont) : Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, _txCanvas_BackBuf[1]); //} //{ Scroll the console for text to go above top of window and don't mix with graphics $ if (con.dwCursorPosition.X) _putch ('\n'); $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top); $ con.srWindow.Top = (short) (con.srWindow.Top + delta); $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta); $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) }; $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY }; $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up $ con.dwCursorPosition.X = 0; $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta); $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window" SetConsoleWindowInfo (out, true, &con.srWindow)) || (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer SetConsoleCursorPosition (out, con.dwCursorPosition)); //} $ txUpdateWindow (true); return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txOK() { return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you? _txCanaryLast == 0x5E2E2E5E && _txCanvas_OK() #if defined (_MSC_VER) && _CrtCheckMemory() #endif ); } //----------------------------------------------------------------------------------------------------------------- //{ Cleanup //----------------------------------------------------------------------------------------------------------------- // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain. // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize(). void _txOnCExit() { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION); _TX_CALLv (Win32::_cexit, ()); } //----------------------------------------------------------------------------------------------------------------- void _txOnExit (int retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode); Win32::exit (retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnExitProcess (unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode); Win32::ExitProcess (retcode); } //----------------------------------------------------------------------------------------------------------------- bool _txOnTerminateProcess (HANDLE process, unsigned retcode) { if (retcode != 0) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode); } $5 _txCleanup(); if (retcode != 0) txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode); return Win32::TerminateProcess (process, retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalExit (int retcode) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode); _TX_CALLv (Win32::FatalExit, (retcode)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode); Win32::TerminateProcess (GetCurrentProcess(), retcode); } //----------------------------------------------------------------------------------------------------------------- void _txOnFatalAppExitA (unsigned action, const char message[]) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message); $5 _txCleanup(); txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message); _TX_CALLv (Win32::FatalAppExitA, (action, message)); txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //----------------------------------------------------------------------------------------------------------------- BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type) { OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type); $5 switch (type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: $ _txExit = true; $ _txCleanup(); case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: default: break; } $ return false; } //----------------------------------------------------------------------------------------------------------------- void _txCleanup() { if (!_txInitialized) return; else _txInitialized = false; $3 _txRunning = false; $ _txConsole_IsBlinking = false; $ HWND canvas = txWindow(); $ HWND console = Win32::GetConsoleWindow(); $ unsigned thread = GetCurrentThreadId(); $ HWND wnd = (canvas)? canvas : console; $ bool externTerm = (thread != _txMainThreadId && thread != _txCanvas_ThreadId); $ DWORD parent = 0; $ int isParentWaitable = _txIsParentWaitable (&parent); $ bool waitableParent = !externTerm && isParentWaitable; $ if (canvas) {$ txSleep (5*_txWindowUpdateInterval); } $ if (_txConsole) { $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY); $ if (console) EnableWindow (console, true); } $ if (_txMain && !externTerm && wnd != NULL) {$ _txSetFinishedText (wnd); } $ _flushall(); $ bool paused = false; $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId)) { $ if (wnd) { if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } $ EnableWindow (wnd, true); } $ if (console && isParentWaitable >= 0) { $ _txPauseBeforeTermination (canvas); $ paused = true; } } $ if (txWindow()) {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); } $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT); $ txSpeak (NULL); $ txPlayVideo (NULL); $ if (GetCurrentThreadId() != _txMainThreadId) {$ SuspendThread (_txMainThread); } $ if (GetCurrentThreadId() != _txCanvas_ThreadId) {$ SuspendThread (_txCanvas_Thread); } $ if (_txMainThread) {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; } $ if (_txCanvas_Thread) {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; } $ if (!txWindow()) {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; } $ console = Win32::GetConsoleWindow(); $ if (_txMain && _txConsole) {$ _txConsole_Detach (waitableParent && !externTerm); } $ bool parentKilled = false; $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT)) { $ parentKilled = _txKillProcess (parent); $ parent = 0; $ if (!parentKilled || _txIsParentWaitable (&parent)) {$ PostMessage (console, WM_CHAR, '\n', 0); } } $ std::cout.flush(); $ std::cerr.flush(); $ std::clog.flush(); $ _flushall(); $ _txSymGetFromAddr (NULL); _TX_ON_DEBUG (OutputDebugString ("\n"); OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n"); OutputDebugString ("\n")); $ if (parentKilled && _txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } } //----------------------------------------------------------------------------------------------------------------- int _txSetFinishedText (HWND wnd) { struct tools { static LRESULT getWindowText (HWND window, wchar_t text[], size_t size) { $3 memset (text, 0, size * sizeof (*text)); $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } static LRESULT setWindowText (HWND window, wchar_t text[]) { $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL); } }; $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib"; $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0; $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len); $ tools::setWindowText (wnd, title); $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1); $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1; $ return 2; } //----------------------------------------------------------------------------------------------------------------- void _txPauseBeforeTermination (HWND canvas) { $3 while (_kbhit()) (void)_getch(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); $ bool kbWait = (_txGetInput() == EOF); $ bool wine = !!Win32::wine_get_version; $ if (kbWait && !canvas && !kbRedir && !wine) { $ txSetLocale(); $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" : "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects $ fflush (stderr); } $ for (int i = 1; ; i++) { $ Sleep (_txWindowUpdateInterval); if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something. if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not. if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas))) {$ TX_ERROR ("Программа зависла и будет завершена."); break; } if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas))) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; } if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL)) {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; } if (!wine && !(i % 100500)) {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); } } $ while (!wine && _kbhit()) (void)_getch(); $ fprintf (stderr, "\n"); } //----------------------------------------------------------------------------------------------------------------- int _txGetInput() { $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE); $ DWORD nChars = 0; $ if (GetConsoleMode (con, &nChars) == 0 && PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL)) { $ return (nChars)? fgetc (stdin) : EOF; } $ if (_kbhit()) { $ return _getch(); } #if defined (_MSC_VER) && (_MSC_VER < 1700) $ if (fseek (stdin, 1, SEEK_CUR) != EOF) { $ (void) fseek (stdin, -1, SEEK_CUR); $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta } #endif $ return EOF; } //----------------------------------------------------------------------------------------------------------------- int _txIsParentWaitable (DWORD* parentPID /*= NULL*/) { $4 PROCESSENTRY32* info = _txFindProcess(); $ if (!info) return 0; $ info = _txFindProcess (info->th32ParentProcessID); $ if (!info) return 0; $ char parent [MAX_PATH] = ""; $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1); $ if (parentPID) *parentPID = info->th32ProcessID; $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS; $ char* ctx = NULL; $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx)) { $ char* gp = strchr (p, ':'); $ if (gp) { $ *gp++ = 0; $ if (_stricmp (p, parent) != 0) { continue; } $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid {$ return islower ((unsigned char) *gp)? +1 : -1; } } else { $ if (_stricmp (p, parent) == 0) {$ return islower ((unsigned char) *p)? +1 : -1; } } } $ return 0; } //----------------------------------------------------------------------------------------------------------------- void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions { $3 if (_TX_ARGUMENT_FAILED (timeout)) return; $ Sleep (*(int*) timeout); $ OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection... _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow")); $ DWORD parent = 0; $ if (_txIsParentWaitable (&parent)) { txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n", _TX_VERSION, __func__, (unsigned long) parent); $ _txKillProcess (parent); $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0); } txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__); $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Tools //----------------------------------------------------------------------------------------------------------------- PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/) { $4 static PROCESSENTRY32 info = { sizeof (info) }; $ if (!pid) return &info; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return NULL; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) if (info.th32ProcessID == pid) break; $ CloseHandle (sshot); $ return &info; } //----------------------------------------------------------------------------------------------------------------- // You are here, little hacker? bool _txKillProcess (DWORD pid) { $3 if (_TX_ARGUMENT_FAILED (pid)) return false; $ HANDLE token = INVALID_HANDLE_VALUE; $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted; $ LUID luid = {}; $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted; $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}}; $ TOKEN_PRIVILEGES old = {}; $ DWORD oldSz = 0; $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted; $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid); $ if (!proc) return false; $ bool ok = !!Win32::TerminateProcess (proc, 0); $ CloseHandle (proc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- int txTaskKill (const char i[] /*= NULL*/, const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/, unsigned x /*= 0*/) { // ...so tired of it already... #define name i // Great name! #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine #define pid x // Another great name, isn't it? $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false; $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L""; if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); } $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); $ assert (sshot); if (!sshot) return 0; $ int killed = 0; $ PROCESSENTRY32 info = { sizeof (info) }; $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info)) { bool kill = false; if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; } if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; } if (!kill) { wchar_t cmdLineW[_TX_BUFSIZE] = L""; if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; } if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; } } if (kill) { $ if (_txKillProcess (info.th32ProcessID)) {$ killed++; } } } $ CloseHandle (sshot); $ return killed; #undef name #undef cmdLine #undef pid } //----------------------------------------------------------------------------------------------------------------- bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/) { $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false; $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false; $ if (pid == (unsigned) _getpid()) { $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1); $ return true; } $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (!proc) {$ return false; } $ Win32::PROCESS_BASIC_INFORMATION pbi = {}; $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted; // Should use ReadProcessMemory() because the info is actually in another address space $ bool ok = true; $ Win32::PEB peb = {}; if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); } $ Win32::RTL_USER_PROCESS_PARAMETERS params = {}; if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); } $ *cmdLine = 0; if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine, MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2), NULL); } $ CloseHandle (proc) asserted; $ return ok; } //----------------------------------------------------------------------------------------------------------------- #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) ) IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/) { $4 assert (module); $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0); $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew); $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE && ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL; } //----------------------------------------------------------------------------------------------------------------- // TXLib continues to hack the reality to make your life better, sweeter and easier uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/, HMODULE module /*= NULL*/, bool debug /*= false*/) { $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module); $ if (_TX_ARGUMENT_FAILED (funcName)) return 0; $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0; $ if (!module) module = GetModuleHandle (NULL); $ if (!module) return 0; $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL; $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL; $ if (useHotPatching && oldFunc) { $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction) $ DWORD oldRights = 0; $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0; // Overwrite oldFunc prolog with JMP trampoline to newFunc. // Calling oldFunc from any location will lead to newFunc call anyway. $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF; $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz); $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights); $ return (uintptr_t) oldFunc; } // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html. $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module); if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; } $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset); $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0; $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL; $ char* impDll = NULL; $ char* impName = NULL; $ void** impPtr = NULL; $ bool found = false; for (; desc->Name; desc++) { $ impDll = RVA_ (char*, module, desc->Name); $ if (dllName && _stricmp (impDll, dllName) != 0) continue; $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk), thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk); thunk0 && thunk1 && thunk1->u1.Function; thunk0++, thunk1++) { impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name; impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName); if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) || (impName && _stricmp (funcName, impName) == 0)) { found = true; break; } } $ if (found) break; } if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n", funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found")); $ if (!found) return 0; $ DWORD rights = PAGE_READWRITE; $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0; $ *(uintptr_t*) impPtr = newFunc; $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights); $ return (uintptr_t) oldFunc; } #undef RVA_ //----------------------------------------------------------------------------------------------------------------- bool _txInDll() { $4 MODULEENTRY32 mod = { sizeof (mod) }; $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); $ assert (sshot); if (!sshot) return false; $ bool inDll = false; $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod)) { $ if (!mod.modBaseAddr) continue; $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr); $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0); $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize)) {$ break; } } $ CloseHandle (sshot); $ return inDll; } //----------------------------------------------------------------------------------------------------------------- bool _txIsConsoleSubsystem() { $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders(); $ return ntHdr && ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI); } //----------------------------------------------------------------------------------------------------------------- bool _txIsBadReadPtr (const void* address) { MEMORY_BASIC_INFORMATION mbi = {}; if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true; if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; return !(mbi.Protect & readRights); } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ Internal TXLib window functions (_txCanvas...) //! @name Внутренние функции окна TXLib (_txCanvas...) //================================================================================================================= unsigned WINAPI _txCanvas_ThreadProc (void* data) { #define SetClassLong_ SetClassLongPtr #define GCL_HICON_ GCLP_HICON #define GCL_HICONSM_ GCLP_HICONSM #define GCL_HCURSOR_ GCLP_HCURSOR $8 _txCanvas_ThreadId = GetCurrentThreadId(); $ if (_TX_ARGUMENT_FAILED (data)) return false; $ unsigned long stackSize = _TX_STACKSIZE; $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize)); $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data); $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0; $ HICON icon32 = LoadIcon (NULL, "_TX_ICON"); $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM"); $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR"); $ HMENU menu = LoadMenu (NULL, "_TX_MENU"); $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS"); $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32))); $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16))); $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW))); if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); } $ Win32::GdiSetBatchLimit (1); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n")); $ SetForegroundWindow (wnd); $ ShowWindow (wnd, SW_SHOW); $ UpdateWindow (wnd); $ _txRunning = true; $ MSG msg = {}; $ while (GetMessage (&msg, NULL, 0, 0)) { if (!msg.hwnd) {$ continue; } if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; } $ TranslateMessage (&msg); $ DispatchMessage (&msg); $ Sleep (0); } $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak. $ LeaveCriticalSection (&_txCanvas_LockBackBuf); _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n")); $ if (_txWatchdogTimeout >= 0) {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); } $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running. { // No chances for good termination, so use exit(). $ _txCleanup(); $ ::exit ((int) msg.wParam); } $ _txCanvas_ThreadId = 0; $ return true; #undef SetClassLong #undef GCL_HICON_ #undef GCL_HICONSM_ #undef GCL_HCURSOR_ } //----------------------------------------------------------------------------------------------------------------- HWND _txCanvas_CreateWindow (const SIZE* sizePtr) { $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL; $ bool centered = false; if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; } $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false); $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0); $ if (!wndClass) return (HWND) NULL; $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN, centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT, centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT, size.cx, size.cy, NULL, NULL, NULL, NULL); $ if (!wnd || !txWindow()) {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; } $ HMENU menu = GetSystemMenu (txWindow(), false); if (!menu) {$ return txWindow(); } $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted; $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted; $ return txWindow(); } //----------------------------------------------------------------------------------------------------------------- const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra) { $8 assert (classId); $ assert (wndProc); $ static char name[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ " _TX_VERSION " " __FILE__ " WndClass %08lX " "-------------[%s]-[TXLib]---*/", classId, (unsigned long) GetTickCount(), classId); $ WNDCLASS wc = { sizeof (wc) }; $ wc.lpszClassName = name; $ wc.lpfnWndProc = wndProc; $ wc.style = style; $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long); $ wc.hCursor = LoadCursor (NULL, IDC_ARROW); $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush); $ ATOM atom = RegisterClass (&wc); if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; } $ return (const char*)(uintptr_t) atom; } //----------------------------------------------------------------------------------------------------------------- inline bool _txCanvas_OK() { return _txCanvas_ThreadId && _txCanvas_Window && _txCanvas_BackBuf[0] && _txCanvas_BackBuf[1] && _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- int _txCanvas_SetRefreshLock (int count) { $8 int oldCount = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = count; $ HWND wnd = txWindow(); $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ return oldCount; } //----------------------------------------------------------------------------------------------------------------- HICON _txCreateTXIcon (int size) { $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL; $ const unsigned char image32 [32*32+1] = "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0" "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0" "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0" "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0" "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0" "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0" "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0" "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000"; $ const unsigned char image16 [16*16+1] = "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990" "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000"; $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0, 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff }; $ const unsigned char* image = (size == 32)? image32 : image16; $ POINT sz = { size, size }; $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask); $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor); $ for (int i = 0; i < size*size; i++) { assert (In (std::nomeow, image[i], '0', '9') || In (std::nomeow, image[i], 'A', 'F')); Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']); } $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP), (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) }; $ HICON icon = CreateIconIndirect (&info); $ assert (icon); $ _txBuffer_Delete (&dcMask) asserted; $ _txBuffer_Delete (&dcColor) asserted; $ return icon; } //} //================================================================================================================= //================================================================================================================= //{ Main window event handlers (_txCanvas_On...) //! @name События основного окна (_txCanvas_On...) //================================================================================================================= //! @{ LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { #if defined (_TX_ALLOW_TRACE) int inTX = _txLoc::Cur.inTX++; if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)", 2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar); _txLoc::Cur.inTX = inTX; #endif $8 if (msg == WM_KEYDOWN && wpar == VK_F12 && GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU)) { $ _txCanvas_OnCmdABOUT (wnd, wpar); $ return DefWindowProc (wnd, msg, wpar, lpar); } WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread if (altWndProc) { $ LRESULT res = altWndProc (wnd, msg, wpar, lpar); $ if (res) return res; } static bool bkErased = false; switch (msg) { case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; } case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; } case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; } case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; } case WM_SIZE: {$ bkErased = false; break; } case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; } case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; } case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; } case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; } case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; } case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; } case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; } case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; } default: break; } if (msg == WM_SYSCOMMAND) switch (wpar) { case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; } case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; } default: break; } $ return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]); $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]); $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0; $ assert (_txCanvas_RefreshTimer); $ _txCanvas_UserDCs = new ::std::vector <HDC>; $ _txCanvas_Window = wnd; $ txSetDefaults(); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROY (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
8027 #endif
8028 
8029 int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue)
8030  {
8031  if (_TX_ARGUMENT_FAILED (keyName)) return 0;
8032 
8033  HKEY hive = NULL;
8034 
8035  #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \
8036  _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 )
8037 
8038  if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE;
8039  else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER;
8040  else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT;
8041  else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS;
8042  else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG;
8043 
8044  else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; }
8045 
8046  #undef EQU_
8047 
8048  keyName = strchr (keyName, '\\') + 1;
8049 
8050  HKEY key = NULL;
8051  DWORD size = 0;
8052 
8053  bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS;
8054  if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS;
8055  if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS;
8056  if (key) ok &= !!RegCloseKey (key);
8057 
8058  return size;
8059  }
8060 
8061 #if defined (_MSC_VER) && (_MSC_VER == 1800)
8062  #pragma warning (pop)
8063 #endif
8064 
8065 //}
8066 //-----------------------------------------------------------------------------------------------------------------
8067 
8068 HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/)
8069  {
8070 $1 if (!_txInitialized) _txInitialized = _txInitialize();
8071 
8072 $ if (HWND wnd = txWindow())
8073  {
8074 $ SetLastErrorEx (ERROR_INVALID_DATA, 0);
8075 $ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!"));
8076 $ return wnd;
8077  }
8078 
8079 $ if (!_txIsDll)
8080  {
8081 $ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe
8082 $ (void) AddAtom ("_txMain");
8083  }
8084 
8085 $ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; }
8086 
8087 $ _txRunning = false;
8088 
8089  // Store the size
8090 
8091 $ static SIZE size = { ROUND (sizeX), ROUND (sizeY) };
8092 $ if (centered) { size.cx *= -1; size.cy *= -1; }
8093 
8094  // In Thread, where REAL creation lies...
8095 
8096 $ unsigned id = 0;
8097 $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id);
8098 
8099 $ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL;
8100 
8101 $ _txWaitFor (_txRunning, 10*_TX_TIMEOUT);
8102 
8103 $ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL;
8104 $ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL;
8105 
8106 $ HWND console = Win32::GetConsoleWindow();
8107 
8108 $ DWORD proc = 0;
8109 $ GetWindowThreadProcessId (console, &proc);
8110 
8111 $ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable()))
8112  {$ ShowWindow (console, _txConsoleMode); }
8113 
8114 $ HMENU menu = GetSystemMenu (txWindow(), false);
8115  if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); }
8116 
8117 $ Win32::GdiSetBatchLimit (1);
8118 
8119 $ SetLastError (0);
8120 
8121 $ errno = 0;
8122 
8123  #if !defined (__CYGWIN__)
8124 $ _doserrno = 0;
8125  #endif
8126 
8127 $ return txWindow();
8128  }
8129 
8130 //-----------------------------------------------------------------------------------------------------------------
8131 
8132 HWND txCreateExtraWindow (CREATESTRUCT createData)
8133  {
8134 $1 if (_TX_TXWINDOW_FAILED()) return NULL;
8135 
8136 $ volatile HWND wnd = NULL;
8137 $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd;
8138 
8139 $ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted;
8140 
8141 $ _txWaitFor (wnd, 5*_TX_TIMEOUT);
8142 
8143 $ return wnd;
8144  }
8145 
8146 //-----------------------------------------------------------------------------------------------------------------
8147 
8148 bool txSetDefaults (HDC dc /*= txDC()*/)
8149  {
8150 $1 if (dc == txDC()) txUpdateWindow (false);
8151 $ txAutoLock _lock;
8152 
8153 $ RECT r = {};
8154 $ GetClientRect (Win32::GetConsoleWindow(), &r) asserted;
8155 $ SIZE szCon = { r.right - r.left, r.bottom - r.top };
8156 
8157 $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
8158 
8159 $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}};
8160 $ GetConsoleScreenBufferInfo (out, &con);
8161 
8162 $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1),
8163  (short) (con.srWindow.Bottom - con.srWindow.Top + 1) };
8164 
8165 //{ Set defaults for graphics layer
8166 
8167 $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted;
8168 $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted;
8169 
8170 $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx,
8171  0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
8172  RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
8173  DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont),
8174  dc) asserted;
8175 
8176 $ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted;
8177 $ Win32::SetBkMode (dc, TRANSPARENT) asserted;
8178 
8179 $ Win32::SetROP2 (dc, R2_COPYPEN) asserted;
8180 $ Win32::SetStretchBltMode (dc, HALFTONE) asserted;
8181 
8182 //}
8183 
8184 $ if (dc != txDC())
8185  {$ return true; }
8186 
8187 //{ Set defaults for console layer
8188 
8189 $ POINT szCanvas = txGetExtent (dc);
8190 
8191 $ HGDIOBJ font = txFontExist (_txConsoleFont)?
8192  Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx,
8193  0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
8194  RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
8195  DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont)
8196  :
8197  Win32::GetStockObject (SYSTEM_FIXED_FONT);
8198 
8199 $ _txBuffer_Select (font, _txCanvas_BackBuf[1]);
8200 //}
8201 
8202 //{ Scroll the console for text to go above top of window and don't mix with graphics
8203 
8204 $ if (con.dwCursorPosition.X) _putch ('\n');
8205 
8206 $ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top);
8207 
8208 $ con.srWindow.Top = (short) (con.srWindow.Top + delta);
8209 $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta);
8210 
8211 $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) };
8212 $ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY };
8213 $ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up
8214 
8215 $ con.dwCursorPosition.X = 0;
8216 $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta);
8217 
8218 $ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window"
8219  SetConsoleWindowInfo (out, true, &con.srWindow))
8220  ||
8221  (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer
8222  SetConsoleCursorPosition (out, con.dwCursorPosition));
8223 //}
8224 
8225 $ txUpdateWindow (true);
8226 
8227  return true;
8228  }
8229 
8230 //-----------------------------------------------------------------------------------------------------------------
8231 
8232 inline bool txOK()
8233  {
8234  return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you?
8235  _txCanaryLast == 0x5E2E2E5E &&
8236  _txCanvas_OK()
8237 
8238  #if defined (_MSC_VER)
8239  && _CrtCheckMemory()
8240  #endif
8241  );
8242  }
8243 
8244 //-----------------------------------------------------------------------------------------------------------------
8245 //{ Cleanup
8246 //-----------------------------------------------------------------------------------------------------------------
8247 
8248 // Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain.
8249 // So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols
8250 // if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize().
8251 
8252 void _txOnCExit()
8253  {
8254  OutputDebugString ("\n");
8255  txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
8256 
8257 $5 _txCleanup();
8258 
8259  txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION);
8260  _TX_CALLv (Win32::_cexit, ());
8261  }
8262 
8263 //-----------------------------------------------------------------------------------------------------------------
8264 
8265 void _txOnExit (int retcode)
8266  {
8267  if (retcode != 0)
8268  {
8269  OutputDebugString ("\n");
8270  txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode);
8271  }
8272 
8273 $5 _txCleanup();
8274 
8275  if (retcode != 0)
8276  txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode);
8277 
8278  Win32::exit (retcode);
8279  }
8280 
8281 //-----------------------------------------------------------------------------------------------------------------
8282 
8283 void _txOnExitProcess (unsigned retcode)
8284  {
8285  if (retcode != 0)
8286  {
8287  OutputDebugString ("\n");
8288  txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode);
8289  }
8290 
8291 $5 _txCleanup();
8292 
8293  if (retcode != 0)
8294  txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode);
8295 
8296  Win32::ExitProcess (retcode);
8297  }
8298 
8299 //-----------------------------------------------------------------------------------------------------------------
8300 
8301 bool _txOnTerminateProcess (HANDLE process, unsigned retcode)
8302  {
8303  if (retcode != 0)
8304  {
8305  OutputDebugString ("\n");
8306  txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode);
8307  }
8308 
8309 $5 _txCleanup();
8310 
8311  if (retcode != 0)
8312  txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode);
8313 
8314  return Win32::TerminateProcess (process, retcode);
8315  }
8316 
8317 //-----------------------------------------------------------------------------------------------------------------
8318 
8319 void _txOnFatalExit (int retcode)
8320  {
8321  OutputDebugString ("\n");
8322  txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode);
8323 
8324 $5 _txCleanup();
8325 
8326  txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode);
8327  _TX_CALLv (Win32::FatalExit, (retcode));
8328 
8329  txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode);
8330  Win32::TerminateProcess (GetCurrentProcess(), retcode);
8331  }
8332 
8333 //-----------------------------------------------------------------------------------------------------------------
8334 
8335 void _txOnFatalAppExitA (unsigned action, const char message[])
8336  {
8337  OutputDebugString ("\n");
8338  txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message);
8339 
8340 $5 _txCleanup();
8341 
8342  txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message);
8343  _TX_CALLv (Win32::FatalAppExitA, (action, message));
8344 
8345  txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION);
8346  Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
8347  }
8348 
8349 //-----------------------------------------------------------------------------------------------------------------
8350 
8351 BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type)
8352  {
8353  OutputDebugString ("\n");
8354  txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type);
8355 
8356 $5 switch (type)
8357  {
8358  case CTRL_LOGOFF_EVENT:
8359  case CTRL_SHUTDOWN_EVENT: $ _txExit = true;
8360  $ _txCleanup();
8361  case CTRL_C_EVENT:
8362  case CTRL_CLOSE_EVENT:
8363  case CTRL_BREAK_EVENT:
8364 
8365  default: break;
8366  }
8367 
8368 $ return false;
8369  }
8370 
8371 //-----------------------------------------------------------------------------------------------------------------
8372 
8373 void _txCleanup()
8374  {
8375  if (!_txInitialized) return;
8376  else _txInitialized = false;
8377 
8378 $3 _txRunning = false;
8379 $ _txConsole_IsBlinking = false;
8380 
8381 $ HWND canvas = txWindow();
8382 $ HWND console = Win32::GetConsoleWindow();
8383 $ unsigned thread = GetCurrentThreadId();
8384 
8385 $ HWND wnd = (canvas)? canvas : console;
8386 
8387 $ bool externTerm = (thread != _txMainThreadId &&
8388  thread != _txCanvas_ThreadId);
8389 $ DWORD parent = 0;
8390 $ int isParentWaitable = _txIsParentWaitable (&parent);
8391 $ bool waitableParent = !externTerm && isParentWaitable;
8392 
8393 $ if (canvas)
8395 
8396 $ if (_txConsole)
8397  {
8398 $ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY);
8399 $ if (console) EnableWindow (console, true);
8400  }
8401 
8402 $ if (_txMain && !externTerm && wnd != NULL)
8403  {$ _txSetFinishedText (wnd); }
8404 
8405 $ _flushall();
8406 
8407 $ bool paused = false;
8408 $ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId))
8409  {
8410 $ if (wnd)
8411  {
8412  if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); }
8413 $ EnableWindow (wnd, true);
8414  }
8415 
8416 $ if (console && isParentWaitable >= 0)
8417  {
8418 $ _txPauseBeforeTermination (canvas);
8419 $ paused = true;
8420  }
8421  }
8422 
8423 $ if (txWindow())
8424  {$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); }
8425 
8426 $ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT);
8427 
8428 $ txSpeak (NULL);
8429 $ txPlayVideo (NULL);
8430 
8431 $ if (GetCurrentThreadId() != _txMainThreadId)
8432  {$ SuspendThread (_txMainThread); }
8433 $ if (GetCurrentThreadId() != _txCanvas_ThreadId)
8434  {$ SuspendThread (_txCanvas_Thread); }
8435 
8436 $ if (_txMainThread)
8437  {$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; }
8438 $ if (_txCanvas_Thread)
8439  {$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; }
8440 
8441 $ if (!txWindow())
8442  {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; }
8443 
8444 $ console = Win32::GetConsoleWindow();
8445 
8446 $ if (_txMain && _txConsole)
8447  {$ _txConsole_Detach (waitableParent && !externTerm); }
8448 
8449 $ bool parentKilled = false;
8450 $ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT))
8451  {
8452 $ parentKilled = _txKillProcess (parent);
8453 
8454 $ parent = 0;
8455 $ if (!parentKilled || _txIsParentWaitable (&parent))
8456  {$ PostMessage (console, WM_CHAR, '\n', 0); }
8457  }
8458 
8459 $ std::cout.flush();
8460 $ std::cerr.flush();
8461 $ std::clog.flush();
8462 $ _flushall();
8463 
8464 $ _txSymGetFromAddr (NULL);
8465 
8466  _TX_ON_DEBUG (OutputDebugString ("\n");
8467  OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n");
8468  OutputDebugString ("\n"));
8469 
8470 $ if (parentKilled && _txWatchdogTimeout >= 0)
8471  {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); }
8472  }
8473 
8474 //-----------------------------------------------------------------------------------------------------------------
8475 
8476 int _txSetFinishedText (HWND wnd)
8477  {
8478  struct tools
8479  {
8480  static LRESULT getWindowText (HWND window, wchar_t text[], size_t size)
8481  {
8482 $3 memset (text, 0, size * sizeof (*text));
8483 
8484 $ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL);
8485  }
8486 
8487  static LRESULT setWindowText (HWND window, wchar_t text[])
8488  {
8489 $1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL);
8490  }
8491  };
8492 
8493 $1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib";
8494 
8495 $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
8496 $ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1;
8497 
8498 $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len);
8499 
8500 $ tools::setWindowText (wnd, title);
8501 $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
8502 $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0;
8503 
8504 $ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len);
8505 
8506 $ tools::setWindowText (wnd, title);
8507 $ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
8508 $ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1;
8509 
8510 $ return 2;
8511  }
8512 
8513 //-----------------------------------------------------------------------------------------------------------------
8514 
8515 void _txPauseBeforeTermination (HWND canvas)
8516  {
8517 $3 while (_kbhit()) (void)_getch();
8518 
8519 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
8520 $ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
8521 $ bool kbWait = (_txGetInput() == EOF);
8522 $ bool wine = !!Win32::wine_get_version;
8523 
8524 $ if (kbWait && !canvas && !kbRedir && !wine)
8525  {
8526 $ txSetLocale();
8527 
8528 $ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" :
8529  "\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects
8530 $ fflush (stderr);
8531  }
8532 
8533 $ for (int i = 1; ; i++)
8534  {
8535 $ Sleep (_txWindowUpdateInterval);
8536 
8537  if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide
8538 
8539  if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something.
8540 
8541  if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not.
8542 
8543  if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed
8544 
8545  if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas)))
8546  {$ TX_ERROR ("Программа зависла и будет завершена."); break; }
8547 
8548  if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas)))
8549  {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; }
8550 
8551  if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL))
8552  {$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; }
8553 
8554  if (!wine && !(i % 100500))
8555  {$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); }
8556  }
8557 
8558 $ while (!wine && _kbhit()) (void)_getch();
8559 
8560 $ fprintf (stderr, "\n");
8561  }
8562 
8563 //-----------------------------------------------------------------------------------------------------------------
8564 
8565 int _txGetInput()
8566  {
8567 $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE);
8568 
8569 $ DWORD nChars = 0;
8570 $ if (GetConsoleMode (con, &nChars) == 0 &&
8571  PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL))
8572  {
8573 $ return (nChars)? fgetc (stdin) : EOF;
8574  }
8575 
8576 $ if (_kbhit())
8577  {
8578 $ return _getch();
8579  }
8580 
8581 #if defined (_MSC_VER) && (_MSC_VER < 1700)
8582 
8583 $ if (fseek (stdin, 1, SEEK_CUR) != EOF)
8584  {
8585 $ (void) fseek (stdin, -1, SEEK_CUR);
8586 $ return fgetc (stdin); // This causes blocking in MSVC 2011 beta
8587  }
8588 
8589 #endif
8590 
8591 $ return EOF;
8592  }
8593 
8594 //-----------------------------------------------------------------------------------------------------------------
8595 
8596 int _txIsParentWaitable (DWORD* parentPID /*= NULL*/)
8597  {
8598 $4 PROCESSENTRY32* info = _txFindProcess();
8599 $ if (!info) return 0;
8600 
8601 $ info = _txFindProcess (info->th32ParentProcessID);
8602 $ if (!info) return 0;
8603 
8604 $ char parent [MAX_PATH] = "";
8605 $ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1);
8606 $ if (parentPID) *parentPID = info->th32ProcessID;
8607 
8608 $ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent
8609 
8610 $ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS;
8611 $ char* ctx = NULL;
8612 
8613 $ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx))
8614  {
8615 $ char* gp = strchr (p, ':');
8616 
8617 $ if (gp)
8618  {
8619 $ *gp++ = 0;
8620 
8621 $ if (_stricmp (p, parent) != 0) { continue; }
8622 
8623 $ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid
8624  {$ return islower ((unsigned char) *gp)? +1 : -1; }
8625  }
8626  else
8627  {
8628 $ if (_stricmp (p, parent) == 0)
8629  {$ return islower ((unsigned char) *p)? +1 : -1; }
8630  }
8631  }
8632 
8633 $ return 0;
8634  }
8635 
8636 //-----------------------------------------------------------------------------------------------------------------
8637 
8638 void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions
8639  {
8640 $3 if (_TX_ARGUMENT_FAILED (timeout)) return;
8641 
8642 $ Sleep (*(int*) timeout);
8643 
8644 $ OutputDebugString ("\n");
8645  txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection...
8646  _TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow"));
8647 $ DWORD parent = 0;
8648 $ if (_txIsParentWaitable (&parent))
8649  {
8650  txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n",
8651  _TX_VERSION, __func__, (unsigned long) parent);
8652 
8653 $ _txKillProcess (parent);
8654 
8655 $ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0);
8656  }
8657 
8658  txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__);
8659 $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
8660  }
8661 
8662 //}
8663 //-----------------------------------------------------------------------------------------------------------------
8664 
8665 //-----------------------------------------------------------------------------------------------------------------
8666 //{ Tools
8667 //-----------------------------------------------------------------------------------------------------------------
8668 
8669 PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/)
8670  {
8671 $4 static PROCESSENTRY32 info = { sizeof (info) };
8672 $ if (!pid) return &info;
8673 
8674 $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
8675 $ assert (sshot); if (!sshot) return NULL;
8676 
8677 $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info))
8678  if (info.th32ProcessID == pid) break;
8679 
8680 $ CloseHandle (sshot);
8681 
8682 $ return &info;
8683  }
8684 
8685 //-----------------------------------------------------------------------------------------------------------------
8686 
8687 // You are here, little hacker?
8688 
8689 bool _txKillProcess (DWORD pid)
8690  {
8691 $3 if (_TX_ARGUMENT_FAILED (pid)) return false;
8692 
8693 $ HANDLE token = INVALID_HANDLE_VALUE;
8694 $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted;
8695 
8696 $ LUID luid = {};
8697 $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted;
8698 
8699 $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}};
8700 $ TOKEN_PRIVILEGES old = {};
8701 
8702 $ DWORD oldSz = 0;
8703 $ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted;
8704 
8705 $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid);
8706 $ if (!proc) return false;
8707 
8708 $ bool ok = !!Win32::TerminateProcess (proc, 0);
8709 $ CloseHandle (proc);
8710 
8711 $ return ok;
8712  }
8713 
8714 //-----------------------------------------------------------------------------------------------------------------
8715 
8716 int txTaskKill (const char i[] /*= NULL*/,
8717  const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/,
8718  unsigned x /*= 0*/)
8719  {
8720  // ...so tired of it already...
8721 
8722  #define name i // Great name!
8723  #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine
8724  #define pid x // Another great name, isn't it?
8725 
8726 $3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false;
8727 
8728 $ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L"";
8729  if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); }
8730 
8731 $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
8732 $ assert (sshot); if (!sshot) return 0;
8733 
8734 $ int killed = 0;
8735 
8736 $ PROCESSENTRY32 info = { sizeof (info) };
8737 $ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info))
8738  {
8739  bool kill = false;
8740 
8741  if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; }
8742 
8743  if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; }
8744 
8745  if (!kill)
8746  {
8747  wchar_t cmdLineW[_TX_BUFSIZE] = L"";
8748  if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; }
8749 
8750  if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; }
8751  }
8752 
8753  if (kill)
8754  {
8755 $ if (_txKillProcess (info.th32ProcessID))
8756  {$ killed++; }
8757  }
8758  }
8759 
8760 $ CloseHandle (sshot);
8761 
8762 $ return killed;
8763 
8764  #undef name
8765  #undef cmdLine
8766  #undef pid
8767  }
8768 
8769 //-----------------------------------------------------------------------------------------------------------------
8770 
8771 bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/)
8772  {
8773 $4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false;
8774 $ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false;
8775 
8776 $ if (pid == (unsigned) _getpid())
8777  {
8778 $ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1);
8779 $ return true;
8780  }
8781 
8782 $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
8783  if (!proc) {$ return false; }
8784 
8785 $ Win32::PROCESS_BASIC_INFORMATION pbi = {};
8786 $ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted;
8787 
8788  // Should use ReadProcessMemory() because the info is actually in another address space
8789 
8790 $ bool ok = true;
8791 
8792 $ Win32::PEB peb = {};
8793  if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); }
8794 
8795 $ Win32::RTL_USER_PROCESS_PARAMETERS params = {};
8796  if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, &params, sizeof (params), NULL); }
8797 
8798 $ *cmdLine = 0;
8799  if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine,
8800  MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2),
8801  NULL); }
8802 $ CloseHandle (proc) asserted;
8803 
8804 $ return ok;
8805  }
8806 
8807 //-----------------------------------------------------------------------------------------------------------------
8808 
8809 #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) )
8810 
8811 IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/)
8812  {
8813 $4 assert (module);
8814 
8815 $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0);
8816 $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew);
8817 
8818 $ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE &&
8819  ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL;
8820  }
8821 
8822 //-----------------------------------------------------------------------------------------------------------------
8823 
8824 // TXLib continues to hack the reality to make your life better, sweeter and easier
8825 
8826 uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/,
8827  HMODULE module /*= NULL*/, bool debug /*= false*/)
8828  {
8829 $4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module);
8830 
8831 $ if (_TX_ARGUMENT_FAILED (funcName)) return 0;
8832 $ if (_TX_ARGUMENT_FAILED (newFunc)) return 0;
8833 
8834 $ if (!module) module = GetModuleHandle (NULL);
8835 $ if (!module) return 0;
8836 
8837 $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL;
8838 $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL;
8839 
8840 $ if (useHotPatching && oldFunc)
8841  {
8842 $ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction)
8843 
8844 $ DWORD oldRights = 0;
8845 $ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0;
8846 
8847  // Overwrite oldFunc prolog with JMP trampoline to newFunc.
8848  // Calling oldFunc from any location will lead to newFunc call anyway.
8849 
8850 $ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel
8851 $ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF;
8852 
8853 $ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz);
8854 
8855 $ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights);
8856 
8857 $ return (uintptr_t) oldFunc;
8858  }
8859 
8860 // For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255
8861 // and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html.
8862 
8863 $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module);
8864  if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; }
8865 
8866 $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
8867 $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset);
8868 
8869 $ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0;
8870 
8871 $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL;
8872 $ char* impDll = NULL;
8873 $ char* impName = NULL;
8874 $ void** impPtr = NULL;
8875 $ bool found = false;
8876 
8877  for (; desc->Name; desc++)
8878  {
8879 $ impDll = RVA_ (char*, module, desc->Name);
8880 $ if (dllName && _stricmp (impDll, dllName) != 0) continue;
8881 
8882 $ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk),
8883  thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk);
8884 
8885  thunk0 && thunk1 && thunk1->u1.Function;
8886 
8887  thunk0++,
8888  thunk1++)
8889  {
8890  impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name;
8891  impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr
8892 
8893  if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName);
8894 
8895  if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) ||
8896  (impName && _stricmp (funcName, impName) == 0))
8897  {
8898  found = true;
8899  break;
8900  }
8901  }
8902 
8903 $ if (found) break;
8904  }
8905 
8906  if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n",
8907  funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found"));
8908 $ if (!found) return 0;
8909 
8910 $ DWORD rights = PAGE_READWRITE;
8911 $ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0;
8912 
8913 $ *(uintptr_t*) impPtr = newFunc;
8914 
8915 $ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights);
8916 
8917 $ return (uintptr_t) oldFunc;
8918  }
8919 
8920 #undef RVA_
8921 
8922 //-----------------------------------------------------------------------------------------------------------------
8923 
8924 bool _txInDll()
8925  {
8926 $4 MODULEENTRY32 mod = { sizeof (mod) };
8927 
8928 $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
8929 $ assert (sshot); if (!sshot) return false;
8930 
8931 $ bool inDll = false;
8932 
8933 $ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod))
8934  {
8935 $ if (!mod.modBaseAddr) continue;
8936 
8937 $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr);
8938 
8939 $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0);
8940 
8941 $ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize))
8942  {$ break; }
8943  }
8944 
8945 $ CloseHandle (sshot);
8946 $ return inDll;
8947  }
8948 
8949 //-----------------------------------------------------------------------------------------------------------------
8950 
8951 bool _txIsConsoleSubsystem()
8952  {
8953 $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders();
8954 
8955 $ return ntHdr &&
8956  ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC &&
8957 
8958  (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
8959  ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI);
8960  }
8961 
8962 //-----------------------------------------------------------------------------------------------------------------
8963 
8964 bool _txIsBadReadPtr (const void* address)
8965  {
8966  MEMORY_BASIC_INFORMATION mbi = {};
8967  if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true;
8968 
8969  if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr
8970 
8971  DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
8972 
8973  return !(mbi.Protect & readRights);
8974  }
8975 
8976 //}
8977 //-----------------------------------------------------------------------------------------------------------------
8978 
8980 //}
8981 //=================================================================================================================
8982 
8983 //=================================================================================================================
8984 //{ Internal TXLib window functions (_txCanvas...)
8986 //=================================================================================================================
8987 
8988 unsigned WINAPI _txCanvas_ThreadProc (void* data)
8989  {
8990  #define SetClassLong_ SetClassLongPtr
8991  #define GCL_HICON_ GCLP_HICON
8992  #define GCL_HICONSM_ GCLP_HICONSM
8993  #define GCL_HCURSOR_ GCLP_HCURSOR
8994 
8995 $8 _txCanvas_ThreadId = GetCurrentThreadId();
8996 
8997 $ if (_TX_ARGUMENT_FAILED (data)) return false;
8998 
8999 $ unsigned long stackSize = _TX_STACKSIZE;
9000 $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize));
9001 
9002 $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data);
9003 $ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0;
9004 
9005 $ HICON icon32 = LoadIcon (NULL, "_TX_ICON");
9006 $ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM");
9007 $ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR");
9008 $ HMENU menu = LoadMenu (NULL, "_TX_MENU");
9009 $ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS");
9010 
9011 $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32)));
9012 $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16)));
9013 $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW)));
9014 
9015  if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); }
9016 
9017 $ Win32::GdiSetBatchLimit (1);
9018 
9019  _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n"));
9020 
9021 $ SetForegroundWindow (wnd);
9022 
9023 $ ShowWindow (wnd, SW_SHOW);
9024 $ UpdateWindow (wnd);
9025 
9026 $ _txRunning = true;
9027 
9028 $ MSG msg = {};
9029 $ while (GetMessage (&msg, NULL, 0, 0))
9030  {
9031  if (!msg.hwnd) {$ continue; }
9032 
9033  if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; }
9034 
9035 $ TranslateMessage (&msg);
9036 $ DispatchMessage (&msg);
9037 
9038 $ Sleep (0);
9039  }
9040 
9041 $ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these
9042 $ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak.
9043 
9044 $ LeaveCriticalSection (&_txCanvas_LockBackBuf);
9045 
9046  _TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n"));
9047 
9048 $ if (_txWatchdogTimeout >= 0)
9049  {$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); }
9050 
9051 $ if (_txRunning && _txMain) // Main window is destroyed but main() is still running.
9052  { // No chances for good termination, so use exit().
9053 $ _txCleanup();
9054 $ ::exit ((int) msg.wParam);
9055  }
9056 
9057 $ _txCanvas_ThreadId = 0;
9058 $ return true;
9059 
9060  #undef SetClassLong
9061  #undef GCL_HICON_
9062  #undef GCL_HICONSM_
9063  #undef GCL_HCURSOR_
9064  }
9065 
9066 //-----------------------------------------------------------------------------------------------------------------
9067 
9068 HWND _txCanvas_CreateWindow (const SIZE* sizePtr)
9069  {
9070 $8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL;
9071 
9072 $ bool centered = false;
9073  if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; }
9074 
9075 $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) };
9076 $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false);
9077 $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top };
9078 
9079 $ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0);
9080 $ if (!wndClass) return (HWND) NULL;
9081 
9082 $ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN,
9083  centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT,
9084  centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT,
9085  size.cx, size.cy, NULL, NULL, NULL, NULL);
9086 $ if (!wnd || !txWindow())
9087  {$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; }
9088 
9089 $ HMENU menu = GetSystemMenu (txWindow(), false);
9090  if (!menu) {$ return txWindow(); }
9091 
9092 $ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted;
9093 $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted;
9094 $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted;
9095 
9096 $ return txWindow();
9097  }
9098 
9099 //-----------------------------------------------------------------------------------------------------------------
9100 
9101 const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra)
9102  {
9103 $8 assert (classId);
9104 $ assert (wndProc);
9105 
9106 $ static char name[_TX_BUFSIZE] = "";
9107 $ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ "
9108  _TX_VERSION " " __FILE__ " WndClass %08lX "
9109  "-------------[%s]-[TXLib]---*/",
9110  classId, (unsigned long) GetTickCount(), classId);
9111 $ WNDCLASS wc = { sizeof (wc) };
9112 
9113 $ wc.lpszClassName = name;
9114 $ wc.lpfnWndProc = wndProc;
9115 $ wc.style = style;
9116 $ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long);
9117 
9118 $ wc.hCursor = LoadCursor (NULL, IDC_ARROW);
9119 $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush);
9120 
9121 $ ATOM atom = RegisterClass (&wc);
9122  if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; }
9123 
9124 $ return (const char*)(uintptr_t) atom;
9125  }
9126 
9127 //-----------------------------------------------------------------------------------------------------------------
9128 
9129 inline bool _txCanvas_OK()
9130  {
9131  return _txCanvas_ThreadId &&
9132  _txCanvas_Window &&
9133  _txCanvas_BackBuf[0] &&
9134  _txCanvas_BackBuf[1] &&
9135  _txCanvas_Pixels;
9136  }
9137 
9138 //-----------------------------------------------------------------------------------------------------------------
9139 
9140 int _txCanvas_SetRefreshLock (int count)
9141  {
9142 $8 int oldCount = _txCanvas_RefreshLock;
9143 
9144 $ _txCanvas_RefreshLock = count;
9145 
9146 $ HWND wnd = txWindow();
9147 
9148 $ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd)
9149  {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
9150 
9151 $ return oldCount;
9152  }
9153 
9154 //-----------------------------------------------------------------------------------------------------------------
9155 
9156 HICON _txCreateTXIcon (int size)
9157  {
9158 $8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL;
9159 
9160 $ const unsigned char image32 [32*32+1] =
9161  "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0"
9162  "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0"
9163  "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0"
9164  "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0"
9165  "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0"
9166  "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0"
9167  "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0"
9168  "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000";
9169 
9170 $ const unsigned char image16 [16*16+1] =
9171  "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990"
9172  "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000";
9173 
9174 $ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0,
9175  0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff };
9176 
9177 $ const unsigned char* image = (size == 32)? image32 : image16;
9178 
9179 $ POINT sz = { size, size };
9180 $ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask);
9181 $ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor);
9182 
9183 $ for (int i = 0; i < size*size; i++)
9184  {
9185  assert (In (std::nomeow, image[i], '0', '9') ||
9186  In (std::nomeow, image[i], 'A', 'F'));
9187 
9188  Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']);
9189  }
9190 
9191 $ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP),
9192  (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) };
9193 
9194 $ HICON icon = CreateIconIndirect (&info);
9195 $ assert (icon);
9196 
9197 $ _txBuffer_Delete (&dcMask) asserted;
9198 $ _txBuffer_Delete (&dcColor) asserted;
9199 
9200 $ return icon;
9201  }
9202 
9203 //}
9204 //=================================================================================================================
9205 
9206 //=================================================================================================================
9207 //{ Main window event handlers (_txCanvas_On...)
9209 //=================================================================================================================
9211 
9212 LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
9213  {
9214 #if defined (_TX_ALLOW_TRACE)
9215 
9216  int inTX = _txLoc::Cur.inTX++;
9217 
9218  if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)",
9219  2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar);
9220  _txLoc::Cur.inTX = inTX;
9221 
9222 #endif
9223 
9224 $8 if (msg == WM_KEYDOWN && wpar == VK_F12 &&
9225  GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU))
9226  {
9227 $ _txCanvas_OnCmdABOUT (wnd, wpar);
9228 $ return DefWindowProc (wnd, msg, wpar, lpar);
9229  }
9230 
9231  WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread
9232  if (altWndProc)
9233  {
9234 $ LRESULT res = altWndProc (wnd, msg, wpar, lpar);
9235 $ if (res) return res;
9236  }
9237 
9238  static bool bkErased = false;
9239 
9240  switch (msg)
9241  {
9242  case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; }
9243 
9244  case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; }
9245  case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; }
9246 
9247  case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; }
9248  case WM_SIZE: {$ bkErased = false; break; }
9249 
9250  case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; }
9251 
9252  case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; }
9253 
9254  case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; }
9255  case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; }
9256 
9257  case WM_LBUTTONUP:
9258  case WM_LBUTTONDOWN:
9259  case WM_RBUTTONUP:
9260  case WM_RBUTTONDOWN:
9261  case WM_MBUTTONUP:
9262  case WM_MBUTTONDOWN:
9263  case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; }
9264 
9265  case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; }
9266 
9267  case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; }
9268  case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; }
9269 
9270  default: break;
9271  }
9272 
9273  if (msg == WM_SYSCOMMAND) switch (wpar)
9274  {
9275  case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; }
9276  case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; }
9277 
9278  default: break;
9279  }
9280 
9281 $ return DefWindowProc (wnd, msg, wpar, lpar);
9282  }
9283 
9284 //-----------------------------------------------------------------------------------------------------------------
9285 
9286 bool _txCanvas_OnCREATE (HWND wnd)
9287  {
9288 $8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
9289 
9290 $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]);
9291 $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]);
9292 
9293 $ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0;
9294 $ assert (_txCanvas_RefreshTimer);
9295 
9296 $ _txCanvas_UserDCs = new ::std::vector <HDC>;
9297 
9298 $ _txCanvas_Window = wnd;
9299 
9300 $ txSetDefaults();
9301 
9302 $ return true;
9303  }
9304 
9305 //-----------------------------------------------------------------------------------------------------------------
9306 
9307 bool _txCanvas_OnDESTROY (HWND wnd)
9308  {
9309 $8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
9310 
9311  // Инициируем остановку цикла сообщений $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS); $ if (!_txCanvas_Window) return false; // Indicate that we are about to manually terminate $ _txExit = true; // Lock GDI resources $ bool locked = false; $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT); $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources"); // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
9312 
9313 $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS);
9314 
9315 $ if (!_txCanvas_Window) return false;
9316 
9317  // Indicate that we are about to manually terminate
9318 
9319 $ _txExit = true;
9320 
9321  // Lock GDI resources
9322 
9323 $ bool locked = false;
9324 $ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT);
9325 $ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources");
9326 
9327  // Освобождаем пользовательские ресурсы $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty()) { $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size()); $ Sleep (_TX_TIMEOUT); $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i)); $ _txCanvas_UserDCs->clear(); } $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL; // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
9328 
9329 $ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty())
9330  {
9331 $ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size());
9332 $ Sleep (_TX_TIMEOUT);
9333 
9334 $ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i));
9335 $ _txCanvas_UserDCs->clear();
9336  }
9337 
9338 $ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL;
9339 
9340  // Освобождаем ресурсы, связанные с окном $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted; $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted; $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted; $ _txCanvas_Pixels = NULL; $ txUnlock(); // Indicate that we are destroyed $ _txCanvas_Window = NULL; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCLOSE (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMain && _txRunning && txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n" "Лучше подождать, когда main() завершится - это отображается в заголовке окна.", txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnTIMER (HWND wnd, WPARAM) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false; $ InvalidateRect (wnd, NULL, false) asserted; $ UpdateWindow (wnd) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnPAINT (HWND wnd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) && GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT); $ PAINTSTRUCT ps = {}; $ HDC wndDc = BeginPaint (wnd, &ps); $ if (!wndDc) return false; $ HDC dc0 = _txCanvas_BackBuf[0], dc1 = _txCanvas_BackBuf[1]; $ RECT r = {}; $ GetClientRect (wnd, &r) asserted; $ POINT wndSize = { r.right - r.left, r.bottom - r.top }; $ POINT dcSize = txGetExtent (dc1); $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) && txLock (false)) { $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY); $ _txConsole_Draw (dc1); $ txUnlock(); } // Magic 100500 value is used to completely block screen refresh. // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL. // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers // themselves. // Yes guys, with all your software installed. :( $ if (_txCanvas_RefreshLock != 100500) { if (_txSwapBuffers) { $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y) { $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY); } else { $ Win32::SetStretchBltMode (wndDc, HALFTONE); $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY); } } $ EndPaint (wnd, &ps) asserted; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk; $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0; $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info) { $8 INPUT_RECORD evt = {}; $ evt.EventType = KEY_EVENT; $ evt.Event.KeyEvent.bKeyDown = true; $ evt.Event.KeyEvent.wRepeatCount = 1; $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch); $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16); $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX $ evt.Event.KeyEvent.dwControlKeyState = 0; $ DWORD written = 0; $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ if (!_txCanvas_OK()) return false; $ if (_txMousePos.x == -1 && _txMousePos.y == -1) { $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT }; $ TrackMouseEvent (&track); } $ _txMousePos.x = LOWORD (coords); $ _txMousePos.y = HIWORD (coords); $ _txMouseButtons = (unsigned) buttons; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnMOUSELEAVE (HWND) { $8 _txMousePos.x = -1; $ _txMousePos.y = -1; $ _txMouseButtons = 0; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar; $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style, create->x, create->y, create->cx, create->cy, create->hwndParent, create->hMenu, NULL, create->lpCreateParams); $ *(HWND*) create->hInstance = wnd; $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar) { $8 if (_TX_ARGUMENT_FAILED (lpar)) return false; $ DestroyWindow ((HWND) lpar); $ return false; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd) { $8 if (_TX_ARGUMENT_FAILED (wnd)) return false; $ HWND console = Win32::GetConsoleWindow(); $ if (!console) return false; $ bool visible = !!IsWindowVisible (console); $ ShowWindow (console, visible? SW_HIDE : SW_SHOW); $ visible = !!IsWindowVisible (console); $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED); $ return true; } //----------------------------------------------------------------------------------------------------------------- bool _txCanvas_OnCmdABOUT (HWND, WPARAM) { $8 //{ Overriding the missing names, if the set is uncomplete #if defined (__MODULE) #define ABOUT_NAME_ __MODULE #else #define ABOUT_NAME_ "TXLib" #endif #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) #ifndef __MODULE #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n" #endif #ifndef __VERSION #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n" #endif #ifndef __DESCRIPTION #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n" #endif #ifndef __AUTHOR #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name." #endif #endif //} $ static char text[_TX_BUFSIZE] = ""; $ _tx_snprintf_s (text, sizeof (text) - 1, "Application:\n\n" #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR) __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n" #else "Здесь могла бы быть Ваша реклама :)\n" "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n" #endif "\n" "%s", _txAppInfo()); $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION); // And a bit of HTTP-code in C++ function: goto http; http://sizeof.livejournal.com $ return true; #undef ABOUT_NAME_ } //! @} //} //================================================================================================================= //================================================================================================================= //{ Console-support functions (_txConsole...) //! @name Функции консольного окна (_txConsole...) //================================================================================================================= //! @{ HWND _txConsole_Attach() { $1 HWND console = Win32::GetConsoleWindow(); $ if (!console) { $ FreeConsole(); $ AllocConsole(); } $ console = Win32::GetConsoleWindow(); $ if (!console) return NULL; $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows $ static bool done = false; $ if (done) return console; $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть. $ if (!_txIsConsoleSubsystem()) {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console // That's all, folks $ done = true; $ return console; } //----------------------------------------------------------------------------------------------------------------- int txSetLocale (int codepage /*= _TX_CODEPAGE*/, const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/) { $1 int oldPage = GetConsoleOutputCP(); // Устанавливаем нужную кодовую страницу для консоли Windows $ if (codepage) { $ SetConsoleCP (codepage); $ SetConsoleOutputCP (codepage); } // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN - // обозначение кодовой страницы (например, для русского языка - CP1251). $ if (locale) { $ setlocale (LC_ALL, locale); $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers } #ifndef __CYGWIN__ $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility. $ if (wLocale && !wine) { $ _wsetlocale (LC_ALL, wLocale); $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above) } #endif (void) wLocale; $ return oldPage; } //----------------------------------------------------------------------------------------------------------------- void txReopenStdio() { $1 // Переоткрываем заново <s>Америку
9341 
9342 $ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted;
9343 
9344 $ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted;
9345 $ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted;
9346 $ _txCanvas_Pixels = NULL;
9347 
9348 $ txUnlock();
9349 
9350  // Indicate that we are destroyed
9351 
9352 $ _txCanvas_Window = NULL;
9353 
9354 $ return true;
9355  }
9356 
9357 //-----------------------------------------------------------------------------------------------------------------
9358 
9359 bool _txCanvas_OnCLOSE (HWND wnd)
9360  {
9361 $8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
9362 $ if (!_txCanvas_OK()) return false;
9363 
9364 $ if (_txMain && _txRunning &&
9365  txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n"
9366  "Лучше подождать, когда main() завершится - это отображается в заголовке окна.",
9367  txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false;
9368 $ return true;
9369  }
9370 
9371 //-----------------------------------------------------------------------------------------------------------------
9372 
9373 bool _txCanvas_OnTIMER (HWND wnd, WPARAM)
9374  {
9375 $8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
9376 
9377 $ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false;
9378 
9379 $ InvalidateRect (wnd, NULL, false) asserted;
9380 $ UpdateWindow (wnd) asserted;
9381 
9382 $ return true;
9383  }
9384 
9385 //-----------------------------------------------------------------------------------------------------------------
9386 
9387 bool _txCanvas_OnPAINT (HWND wnd)
9388  {
9389 $8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
9390 $ if (!_txCanvas_OK()) return false;
9391 
9392 $ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) &&
9393  GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT);
9394 
9395 $ PAINTSTRUCT ps = {};
9396 $ HDC wndDc = BeginPaint (wnd, &ps);
9397 $ if (!wndDc) return false;
9398 
9399 $ HDC dc0 = _txCanvas_BackBuf[0],
9400  dc1 = _txCanvas_BackBuf[1];
9401 
9402 $ RECT r = {};
9403 $ GetClientRect (wnd, &r) asserted;
9404 $ POINT wndSize = { r.right - r.left, r.bottom - r.top };
9405 
9406 $ POINT dcSize = txGetExtent (dc1);
9407 
9408 $ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) &&
9409  txLock (false))
9410  {
9411 $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY);
9412 
9413 $ _txConsole_Draw (dc1);
9414 
9415 $ txUnlock();
9416  }
9417 
9418  // Magic 100500 value is used to completely block screen refresh.
9419  // Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL.
9420  // Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers
9421  // themselves.
9422  // Yes guys, with all your software installed. :(
9423 
9424 $ if (_txCanvas_RefreshLock != 100500)
9425  {
9426  if (_txSwapBuffers)
9427  {
9428 $ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY);
9429  }
9430  else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y)
9431  {
9432 $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY);
9433  }
9434  else
9435  {
9436 $ Win32::SetStretchBltMode (wndDc, HALFTONE);
9437 $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY);
9438  }
9439  }
9440 
9441 $ EndPaint (wnd, &ps) asserted;
9442 
9443 $ return true;
9444  }
9445 
9446 //-----------------------------------------------------------------------------------------------------------------
9447 
9448 bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info)
9449  {
9450 $8 INPUT_RECORD evt = {};
9451 
9452 $ evt.EventType = KEY_EVENT;
9453 $ evt.Event.KeyEvent.bKeyDown = true;
9454 $ evt.Event.KeyEvent.wRepeatCount = 1;
9455 $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR
9456 $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16);
9457 $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk;
9458 $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0;
9459 
9460 $ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job
9461 
9462 $ DWORD written = 0;
9463 $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written);
9464 
9465 $ return false;
9466  }
9467 
9468 //-----------------------------------------------------------------------------------------------------------------
9469 
9470 bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info)
9471  {
9472 $8 INPUT_RECORD evt = {};
9473 
9474 $ evt.EventType = KEY_EVENT;
9475 $ evt.Event.KeyEvent.bKeyDown = true;
9476 $ evt.Event.KeyEvent.wRepeatCount = 1;
9477 $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch);
9478 $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16);
9479 $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX
9480 $ evt.Event.KeyEvent.dwControlKeyState = 0;
9481 
9482 $ DWORD written = 0;
9483 $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written);
9484 
9485 $ return true;
9486  }
9487 
9488 //-----------------------------------------------------------------------------------------------------------------
9489 
9490 bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords)
9491  {
9492 $8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
9493 $ if (!_txCanvas_OK()) return false;
9494 
9495 $ if (_txMousePos.x == -1 && _txMousePos.y == -1)
9496  {
9497 $ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT };
9498 $ TrackMouseEvent (&track);
9499  }
9500 
9501 $ _txMousePos.x = LOWORD (coords);
9502 $ _txMousePos.y = HIWORD (coords);
9503 $ _txMouseButtons = (unsigned) buttons;
9504 
9505 $ return true;
9506  }
9507 
9508 //-----------------------------------------------------------------------------------------------------------------
9509 
9510 bool _txCanvas_OnMOUSELEAVE (HWND)
9511  {
9512 $8 _txMousePos.x = -1;
9513 $ _txMousePos.y = -1;
9514 $ _txMouseButtons = 0;
9515 
9516 $ return true;
9517  }
9518 
9519 //-----------------------------------------------------------------------------------------------------------------
9520 
9521 bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar)
9522  {
9523 $8 if (_TX_ARGUMENT_FAILED (lpar)) return false;
9524 
9525 $ const CREATESTRUCT* create = (CREATESTRUCT*) lpar;
9526 
9527 $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style,
9528  create->x, create->y, create->cx, create->cy,
9529  create->hwndParent, create->hMenu, NULL, create->lpCreateParams);
9530 
9531 $ *(HWND*) create->hInstance = wnd;
9532 
9533 $ return true;
9534  }
9535 
9536 //-----------------------------------------------------------------------------------------------------------------
9537 
9538 bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar)
9539  {
9540 $8 if (_TX_ARGUMENT_FAILED (lpar)) return false;
9541 
9542 $ DestroyWindow ((HWND) lpar);
9543 
9544 $ return false;
9545  }
9546 
9547 //-----------------------------------------------------------------------------------------------------------------
9548 
9549 bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd)
9550  {
9551 $8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
9552 
9553 $ HWND console = Win32::GetConsoleWindow();
9554 $ if (!console) return false;
9555 
9556 $ bool visible = !!IsWindowVisible (console);
9557 
9558 $ ShowWindow (console, visible? SW_HIDE : SW_SHOW);
9559 
9560 $ visible = !!IsWindowVisible (console);
9561 $ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED);
9562 
9563 $ return true;
9564  }
9565 
9566 //-----------------------------------------------------------------------------------------------------------------
9567 
9568 bool _txCanvas_OnCmdABOUT (HWND, WPARAM)
9569  {
9570 $8 //{ Overriding the missing names, if the set is uncomplete
9571 
9572  #if defined (__MODULE)
9573  #define ABOUT_NAME_ __MODULE
9574  #else
9575  #define ABOUT_NAME_ "TXLib"
9576  #endif
9577 
9578  #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR)
9579 
9580  #ifndef __MODULE
9581  #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n"
9582  #endif
9583 
9584  #ifndef __VERSION
9585  #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n"
9586  #endif
9587 
9588  #ifndef __DESCRIPTION
9589  #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n"
9590  #endif
9591 
9592  #ifndef __AUTHOR
9593  #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name."
9594  #endif
9595 
9596  #endif
9597  //}
9598 
9599 $ static char text[_TX_BUFSIZE] = "";
9600 
9601 $ _tx_snprintf_s (text, sizeof (text) - 1,
9602 
9603  "Application:\n\n"
9604 
9605  #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR)
9606  __MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n"
9607  #else
9608  "Здесь могла бы быть Ваша реклама :)\n"
9609  "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n"
9610  #endif
9611 
9612  "\n" "%s", _txAppInfo());
9613 
9614 $ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION);
9615 
9616  // And a bit of HTTP-code in C++ function:
9617 
9618  goto http;
9619  http://sizeof.livejournal.com
9620 
9621 $ return true;
9622 
9623  #undef ABOUT_NAME_
9624  }
9625 
9627 //}
9628 //=================================================================================================================
9629 
9630 //=================================================================================================================
9631 //{ Console-support functions (_txConsole...)
9633 //=================================================================================================================
9635 
9636 HWND _txConsole_Attach()
9637  {
9638 $1 HWND console = Win32::GetConsoleWindow();
9639 
9640 $ if (!console)
9641  {
9642 $ FreeConsole();
9643 $ AllocConsole();
9644  }
9645 
9646 $ console = Win32::GetConsoleWindow();
9647 $ if (!console) return NULL;
9648 
9649 $ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows
9650 
9651 $ static bool done = false;
9652 $ if (done) return console;
9653 
9654 $ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть.
9655 
9656 $ if (!_txIsConsoleSubsystem())
9657  {$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console
9658 
9659  // That's all, folks
9660 
9661 $ done = true;
9662 $ return console;
9663  }
9664 
9665 //-----------------------------------------------------------------------------------------------------------------
9666 
9667 int txSetLocale (int codepage /*= _TX_CODEPAGE*/,
9668  const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/)
9669  {
9670 $1 int oldPage = GetConsoleOutputCP();
9671 
9672  // Устанавливаем нужную кодовую страницу для консоли Windows
9673 
9674 $ if (codepage)
9675  {
9676 $ SetConsoleCP (codepage);
9677 $ SetConsoleOutputCP (codepage);
9678  }
9679 
9680  // Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии
9681  // функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным
9682  // языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN -
9683  // обозначение кодовой страницы (например, для русского языка - CP1251).
9684 
9685 $ if (locale)
9686  {
9687 $ setlocale (LC_ALL, locale);
9688 $ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers
9689  }
9690 
9691  #ifndef __CYGWIN__
9692 
9693 $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility.
9694 
9695 $ if (wLocale && !wine)
9696  {
9697 $ _wsetlocale (LC_ALL, wLocale);
9698 $ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above)
9699  }
9700 
9701  #endif
9702 
9703  (void) wLocale;
9704 
9705 $ return oldPage;
9706  }
9707 
9708 //-----------------------------------------------------------------------------------------------------------------
9709 
9710 void txReopenStdio()
9711  {
9712 $1 // Переоткрываем заново <s>Америку
9713 
9714  #ifndef __CYGWIN__
9715 
9716 $ *stdin = *_fdopen (_open_osfhandle ((intptr_t) GetStdHandle (STD_INPUT_HANDLE), _O_TEXT), "r");
9717 $ fflush (stdout); *stdout = *_fdopen (_open_osfhandle ((intptr_t) GetStdHandle (STD_OUTPUT_HANDLE), _O_TEXT), "w");
9718 $ fflush (stderr); *stderr = *_fdopen (_open_osfhandle ((intptr_t) GetStdHandle (STD_ERROR_HANDLE), _O_TEXT), "w");
9719 
9720  #else
9721 $ *stdin = *_fdopen (STDIN_FILENO, "r");
9722 $ fflush (stdout); *stdout = *_fdopen (STDOUT_FILENO, "w");
9723 $ fflush (stderr); *stderr = *_fdopen (STDERR_FILENO, "w");
9724 
9725  #endif
9726 
9727 $ setvbuf (stdin, NULL, _IONBF, 0);
9728 $ setvbuf (stdout, NULL, _IONBF, 0);
9729 $ setvbuf (stderr, NULL, _IONBF, 0);
9730 
9731 $ ::std::ios::sync_with_stdio();
9732  }
9733 
9734 //-----------------------------------------------------------------------------------------------------------------
9735 
9736 inline bool _txConsole_OK()
9737  {
9738  return Win32::GetConsoleWindow() != NULL;
9739  }
9740 
9741 //-----------------------------------------------------------------------------------------------------------------
9742 
9743 bool _txConsole_Detach (bool activate)
9744  {
9745 $1 HWND console = Win32::GetConsoleWindow();
9746 $ if (!console) return false;
9747 
9748 $ EnableWindow (console, true);
9749 $ ShowWindow (console, SW_SHOW);
9750 
9751 $ if (activate)
9752  {
9753 $ SetForegroundWindow (console);
9754 $ BringWindowToTop (console);
9755  }
9756 
9757 $ return !!FreeConsole();
9758  }
9759 
9760 //-----------------------------------------------------------------------------------------------------------------
9761 
9762 bool _txConsole_Draw (HDC dc)
9763  {
9764 $8 if (_TX_HDC_FAILED (dc)) return false;
9765 
9766 $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
9767 
9768 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
9769 $ BOOL ok = GetConsoleScreenBufferInfo (out, &con);
9770 $ if (!ok) return false;
9771 
9772 $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1,
9773  con.srWindow.Bottom - con.srWindow.Top + 1 };
9774 
9775 $ SIZE fontSz = { 12, 16 };
9776 $ Win32::GetTextExtentPoint32 (dc, "W", 1, &fontSz) asserted;
9777 
9778 $ COLORREF pal [16] = { 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0,
9779  0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF };
9780 
9781 $ for (short y = 0; y < size.y; y++)
9782  {
9783  static char chr [_TX_BUFSIZE + 1] = ""; // [con.dwSize.X + 1]; maybe will be truncated
9784  static WORD atr [_TX_BUFSIZE + 1] = {}; // [con.dwSize.X + 1]; maybe will be truncated
9785  COORD coord = { (short) (con.srWindow.Left), (short) (y + con.srWindow.Top) };
9786  DWORD read = 0;
9787 
9788  if (!ReadConsoleOutputCharacter (out, chr, sizearr (chr) - 1, coord, &read)) continue;
9789  if (!ReadConsoleOutputAttribute (out, atr, sizearr (atr) - 1, coord, &read)) continue;
9790 
9791  for (int x = 0, xEnd = size.x; x < size.x; x = xEnd)
9792  {
9793  Win32::SetTextColor (dc, pal [ atr[x] & 0x0F]);
9794  Win32::SetBkColor (dc, pal [(atr[x] >> 4) & 0x0F]);
9795  Win32::SetBkMode (dc, (atr[x] & 0xF0)? OPAQUE : TRANSPARENT);
9796 
9797  for (xEnd = x+1; xEnd < size.x && atr[xEnd] == atr[x]; xEnd++) {;}
9798 
9799  Win32::TextOut (dc, ROUND (fontSz.cx * (x + con.srWindow.Left)),
9800  ROUND (fontSz.cy * y), chr + x, xEnd - x) asserted;
9801  }
9802  }
9803 
9804 $ Win32::SetTextColor (dc, pal [ con.wAttributes & 0x0F]);
9805 $ Win32::SetBkColor (dc, pal [(con.wAttributes >> 4) & 0x0F]);
9806 $ Win32::SetBkMode (dc, TRANSPARENT);
9807 
9808 $ if (_txConsole_IsBlinking &&
9809  In (con.dwCursorPosition, con.srWindow) &&
9810  GetTickCount() % _txCursorBlinkInterval*2 > _txCursorBlinkInterval &&
9811  GetForegroundWindow() == txWindow())
9812  {
9813 $ Win32::TextOut (dc, ROUND (fontSz.cx * (con.dwCursorPosition.X - con.srWindow.Left)),
9814  ROUND (fontSz.cy * (con.dwCursorPosition.Y - con.srWindow.Top)) + 1,
9815  "_", 1) asserted;
9816  }
9817 
9818 $ return true;
9819  }
9820 
9821 //-----------------------------------------------------------------------------------------------------------------
9822 //{ Welcome to the Duck Side! Together we will rule the Bathroom!
9823 //-----------------------------------------------------------------------------------------------------------------
9824 
9825 bool _txConsole_SetUnicodeFont()
9826  {
9827 $ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility.
9828  // Beer compatibility may be added in future versions...
9829 $ if (wine) // Минздрав РФ предупреждает: чрезмерное употребление wine
9830  { // вредит Вашему здоровью.
9831 $ Win32::GetNumberOfConsoleFonts = NULL;
9832 $ Win32::GetCurrentConsoleFont = NULL;
9833 $ Win32::SetConsoleFont = NULL;
9834 
9835 $ return false;
9836  }
9837 
9838  // Начиная с Висты все хорошо...
9839 
9840 $1 if (Win32::GetCurrentConsoleFontEx && Win32::SetCurrentConsoleFontEx)
9841  {
9842 $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
9843 
9844 $ Win32::CONSOLE_FONT_INFOEX info = { sizeof (info) };
9845 $ if (!Win32::GetCurrentConsoleFontEx (out, false, &info)) return false;
9846 
9847 $ info.FontFamily = 0x36; // Unicode fixed-pitch
9848 $ if (!*info.FaceName) info.dwFontSize.Y = (SHORT) (info.dwFontSize.Y + 2); // Terminal font is too small
9849 
9850 $ if (wcsncmp (info.FaceName, L"Consolas", sizearr (info.FaceName)) != 0) // Consolas is allowed too
9851  {$ wcsncpy_s (info.FaceName, sizearr (info.FaceName), L"Lucida Console", sizearr (info.FaceName)); }
9852 
9853 $ return !!Win32::SetCurrentConsoleFontEx (out, false, &info);
9854  }
9855 
9856  // ...а до этого все не так сладко
9857 
9858 $ const unsigned uniFont = 10; // The Internet and W2K sources know this magic number
9859 $ const unsigned uniSize = 20; // Size of the font desired, should be > max # of Raster Fonts
9860 $ bool ok = true;
9861 
9862  // Force Windows to use Unicode font by creating and run the console shortcut tuned to use that font.
9863 
9864 $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
9865 
9866 $ unsigned fonts = _TX_CALL (Win32::GetNumberOfConsoleFonts, ());
9867 $ if (fonts && fonts <= uniFont)
9868  {
9869 $ HRESULT init = Win32::CoInitialize (NULL);
9870 $ size_t sz = 0;
9871 
9872 $ char link [MAX_PATH] = "";
9873 $ getenv_s (&sz, link, sizeof (link) - 1, "TEMP");
9874 $ strncat_s (link, sizeof (link), "\\~txLink.lnk", sizeof (link) - 1);
9875 
9876 $ char comspec [MAX_PATH] = "";
9877 $ getenv_s (&sz, comspec, sizeof (comspec), "COMSPEC");
9878 
9879 $ (void) _unlink (link);
9880 
9881 $ _txCreateShortcut (link, comspec, "/c exit", NULL, NULL, SW_SHOWMINNOACTIVE, NULL, 0, uniSize) asserted;
9882 
9883 $ ok = (Win32::ShellExecuteA (NULL, NULL, link, NULL, NULL, SW_SHOWMINNOACTIVE) > (void*)32); // Sic!
9884  if (ok) {$ _txWaitFor (FindWindow (NULL, "~txLink"), _TX_TIMEOUT); }
9885 
9886 $ (void) _unlink (link);
9887 
9888 $ if (init == S_OK) Win32::CoUninitialize();
9889  }
9890 
9891  // If Unicode font is not already set, do set it.
9892 
9893 $ Win32::CONSOLE_FONT_INFO cur = {};
9894 $ _TX_CALL (Win32::GetCurrentConsoleFont, (out, false, &cur));
9895 
9896 $ ok &= (cur.nFont >= uniFont);
9897  if (!ok) {$ ok &= _TX_CALL (Win32::SetConsoleFont, (out, uniFont)); }
9898 
9899 $ HWND console = Win32::GetConsoleWindow();
9900 $ InvalidateRect (console, NULL, false);
9901 $ UpdateWindow (console);
9902 
9903 $ return ok;
9904  }
9905 
9906 //-----------------------------------------------------------------------------------------------------------------
9907 //{ The assistants to the nightmare. You can use it freely to make your own nightmare sweet.
9908 
9909 #define _TX_TRY { goto __tx_try; } __tx_try: { int __tx_error = S_OK; (void)__tx_error;
9910 #define _TX_CHECKED( cmd ) { if (FAILED (__tx_error = (cmd))) goto __tx_catch; }
9911 #define _TX_FAIL { __tx_error = E_FAIL; goto __tx_catch; }
9912 #define _TX_RETRY { __tx_error = S_OK; goto __tx_try; }
9913 #define _TX_OK ( SUCCEEDED (__tx_error) )
9914 #define _TX_CATCH goto __tx_finally; __tx_catch:
9915 #define _TX_RETURN goto __tx_finally;
9916 #define _TX_FINALLY __tx_finally:
9917 #define _TX_ENDTRY }
9918 
9919 //}
9920 //-----------------------------------------------------------------------------------------------------------------
9921 
9922 // Мало не покажется
9923 
9924 bool _txCreateShortcut (const char shortcutName[],
9925  const char fileToLink[], const char args[] /*= NULL*/, const char workDir[] /*= NULL*/,
9926  const char description[] /*= NULL*/, int cmdShow /*= SW_SHOWNORMAL*/, const char iconFile[] /*= NULL*/, int iconIndex /*= 0*/,
9927  int fontSize /*= 0*/, COORD bufSize /*= ZERO (COORD)*/, COORD wndSize /*= ZERO (COORD)*/, COORD wndOrg /*=ZERO (COORD)*/)
9928  {
9929 $1 if (_TX_ARGUMENT_FAILED (shortcutName && *shortcutName)) return false;
9930 $ if (_TX_ARGUMENT_FAILED (fileToLink && *fileToLink)) return false;
9931 
9932 $ IShellLink* shellLink = NULL;
9933 $ Win32::IShellLinkDataList* dataList = NULL;
9934 $ IPersistFile* file = NULL;
9935 
9936 $ HRESULT init = Win32::CoInitialize (NULL);
9937 
9938  _TX_TRY
9939  {
9940 $ _TX_CHECKED (Win32::CoCreateInstance (CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, Win32::IID_IShellLink, (void**) &shellLink));
9941 $ if (!shellLink) _TX_FAIL;
9942 
9943 $ shellLink->SetPath (fileToLink);
9944 $ shellLink->SetArguments (args);
9945 $ shellLink->SetWorkingDirectory (workDir);
9946 $ shellLink->SetDescription (description);
9947 $ shellLink->SetShowCmd (cmdShow);
9948 $ shellLink->SetIconLocation (iconFile, iconIndex);
9949 
9950 $ _TX_CHECKED (shellLink->QueryInterface (Win32::IID_IShellLinkDataList, (void**) &dataList));
9951 $ if (!dataList) _TX_FAIL;
9952 
9953 $ Win32::NT_CONSOLE_PROPS props =
9954  {{sizeof (props), NT_CONSOLE_PROPS_SIG},
9955 
9956  FOREGROUND_LIGHTGRAY, // wFillAttribute
9957  FOREGROUND_MAGENTA | BACKGROUND_WHITE, // wPopupFillAttribute
9958  {bufSize.X, bufSize.Y}, // dwScreenBufferSize
9959  {wndSize.X, wndSize.Y}, // dwWindowSize
9960  {wndOrg.X, wndOrg.Y}, // dwWindowOrigin
9961  0, // nFont
9962  0, // nInputBufferSize
9963  {0, (short) fontSize}, // dwFontSize
9964  0x36, 400, L"Lucida Console", // uFontFamily, uFontWeight, FaceName. We're dancing for this!
9965  15, // uCursorSize
9966  0, 1, 1, 0, // bFullScreen, bQuickEdit, bInsertMode, bAutoPosition
9967  50, 4, 0, // uHistoryBufferSize, uNumberOfHistoryBuffers, bHistoryNoDup
9968 
9969  {0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0, // Palette
9970  0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF}
9971  };
9972 
9973 $ _TX_CHECKED (dataList->AddDataBlock (&props));
9974 
9975 $ _TX_CHECKED (shellLink->QueryInterface (Win32::IID_IPersistFile, (void**) &file));
9976 $ if (!file) _TX_FAIL;
9977 
9978 $ wchar_t wName[MAX_PATH] = L"";
9979 $ MultiByteToWideChar (_TX_CODEPAGE, 0, shortcutName, -1, wName, MAX_PATH) || ZeroMemory (wName, sizeof (wName));
9980 
9981 $ _TX_CHECKED (file->Save (wName, true));
9982  }
9983 
9984 $ _TX_CATCH
9985 $ _TX_FINALLY
9986 
9987 $ if (file) file ->Release();
9988 $ if (dataList) dataList ->Release();
9989 $ if (shellLink) shellLink->Release();
9990 
9991 $ if (init == S_OK) Win32::CoUninitialize();
9992 
9993 $ return _TX_OK;
9994  _TX_ENDTRY
9995  }
9996 
9997 //}
9998 //-----------------------------------------------------------------------------------------------------------------
9999 
10001 //}
10002 //=================================================================================================================
10003 
10004 //=================================================================================================================
10005 //{ Memory DC functions (_txBuffer...)
10007 //=================================================================================================================
10009 
10010 HDC _txBuffer_Create (HWND wnd, const POINT* size /*= NULL*/, HBITMAP bitmap /*= NULL*/, RGBQUAD** pixels /*= NULL*/)
10011  {
10012 $1 txAutoLock _lock;
10013 
10014 $ HDC wndDC = GetDC (wnd);
10015 $ if (!wndDC) return NULL;
10016 
10017 $ POINT sz = { 1, 1 };
10018 $ if (size) sz = *size;
10019 
10020 $ if (!size && wnd)
10021  {
10022 $ RECT r = {};
10023 $ GetClientRect (wnd, &r) asserted;
10024 
10025 $ sz.x = r.right - r.left;
10026 $ sz.y = r.bottom - r.top;
10027  }
10028 
10029 $ if (bitmap)
10030  {
10031 $ BITMAP bmap = {};
10032 $ Win32::GetObject (bitmap, sizeof (bmap), &bmap) asserted;
10033 
10034 $ sz.x = bmap.bmWidth;
10035 $ sz.y = bmap.bmHeight;
10036  }
10037 
10038 $ HDC dc = Win32::CreateCompatibleDC (wndDC);
10039 $ if (!dc) TX_DEBUG_ERROR ("Cannot create buffer: CreateCompatibleDC() failed");
10040 
10041 $ BITMAPINFO info = {{ sizeof (info), sz.x, sz.y, 1, WORD (sizeof (RGBQUAD) * 8), BI_RGB }};
10042 
10043 $ HBITMAP bmap = bitmap? bitmap : Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (void**) pixels, NULL, 0);
10044 $ if (!bmap) TX_DEBUG_ERROR ("Cannot create buffer: CreateCompatibleBitmap() failed");
10045 
10046 $ Win32::SelectObject (dc, bmap) asserted;
10047 
10048 $ if (!bitmap) Win32::PatBlt (dc, 0, 0, sz.x, sz.y, BLACKNESS) asserted;
10049 
10050 $ ReleaseDC (wnd, wndDC) asserted;
10051 
10052 $ return dc;
10053  }
10054 
10055 //-----------------------------------------------------------------------------------------------------------------
10056 
10057 bool _txBuffer_Delete (HDC* dc)
10058  {
10059 $1 if (_TX_ARGUMENT_FAILED (dc)) return false;
10060 $ if (!*dc) return false;
10061 
10062 $ if (_TX_HDC_FAILED (*dc)) return false;
10063 
10064 $ if (!Win32::GetObjectType (Win32::GetCurrentObject (*dc, OBJ_BITMAP))) return false;
10065 
10066 $ txAutoLock _lock;
10067 
10068 $ _txBuffer_Select (Win32::GetStockObject (NULL_PEN), *dc) asserted;
10069 $ _txBuffer_Select (Win32::GetStockObject (NULL_BRUSH), *dc) asserted;
10070 $ _txBuffer_Select (Win32::GetStockObject (SYSTEM_FONT), *dc) asserted;
10071 $ _txBuffer_Select (_txStockBitmap, *dc);
10072 
10073 $ Win32::DeleteObject (Win32::GetCurrentObject (*dc, OBJ_BITMAP)) asserted;
10074 
10075 $ Win32::DeleteDC (*dc) asserted;
10076 
10077 $ *dc = NULL;
10078 
10079 $ return true;
10080  }
10081 
10082 //-----------------------------------------------------------------------------------------------------------------
10083 
10084 bool _txBuffer_Select (HGDIOBJ obj, HDC dc /*= txDC()*/)
10085  {
10086 $1 if (_TX_ARGUMENT_FAILED (obj)) return false;
10087 $ if (_TX_HDC_FAILED (dc)) return false;
10088 
10089 $ if (!Win32::GetObjectType (obj)) TX_DEBUG_ERROR ("Invalid GDI object type");
10090 
10091 $ txAutoLock _lock;
10092 
10093 $ obj = Win32::SelectObject (dc, obj);
10094 $ if (obj) Win32::DeleteObject (obj);
10095 
10096 $ return obj != NULL;
10097  }
10098 
10100 //}
10101 //=================================================================================================================
10102 
10103 //=================================================================================================================
10104 //{ Diagnostics
10106 //=================================================================================================================
10108 
10109 const char* _txError (const char file[] /*= NULL*/, int line /*= 0*/, const char func[] /*= NULL*/, unsigned color /*= 0*/,
10110  const char msg [] /*= NULL*/, ...)
10111  { //---/\---/\-------Это ASCII KOT!--//
10112 $1 va_list arg; va_start (arg, msg); // { '-' } //
10113 $$ const char* what = _txProcessError (file, line, func, color, msg, arg); // { 0 0 } Добавь его себе //
10114  va_end (arg); // --> V <-- в исходник, и тебе //
10115  // \ \|/ / будет, наверно, //
10116  if (!(msg && msg[0] == '\a')) return what; // \___/ приятно отлаживаться //
10117  //---------------долгими ночами:)--//
10118 // vvvvvvvvvvvvvvvvvv
10119  DebugBreak(); //>>> Котики, вы в отладчике. Не пугайтесь. Есть шанс посмотреть переменные и разобраться.
10120 // ^^^^^^^^^^^^^^^^^^
10121 
10122  return what; //>>> Уходите из функции пошаговой отладкой (F10/F11). Следите за стеком вызовов (Alt+7).
10123  }
10124 
10125 //-----------------------------------------------------------------------------------------------------------------
10126 //{ General runtime check hooks
10127 //-----------------------------------------------------------------------------------------------------------------
10128 
10129 void _txOnSignal (int sig /*= 0*/, int fpe /*= 0*/)
10130  {
10131 $1 if (!sig && !fpe)
10132  {
10133 $ signal (SIGSEGV, (void(*)(int))(uintptr_t)_txOnSignal) != SIG_ERR asserted;
10134 $ signal (SIGFPE, (void(*)(int))(uintptr_t)_txOnSignal) != SIG_ERR asserted;
10135 $ signal (SIGABRT, (void(*)(int))(uintptr_t)_txOnSignal) != SIG_ERR asserted;
10136 $ signal (SIGILL, (void(*)(int))(uintptr_t)_txOnSignal) != SIG_ERR asserted;
10137 $ signal (SIGTERM, (void(*)(int))(uintptr_t)_txOnSignal) != SIG_ERR asserted;
10138 $ return;
10139  }
10140 
10141  txOutputDebugPrintf ("%s - WARNING: %s (%d, %d) called\n", _TX_VERSION, __func__, sig, fpe);
10142 
10143  #define GET_DESCR_(str, code, descr) case (code): {$ (str) = " " #code ": " descr; break; }
10144 
10145 $ const char* sSig = "Неизвестный тип сигнала; $ switch (sig) { GET_DESCR_ (sSig, SIGSEGV, "Доступ по неверному указателю. Ставьте ассерты!") GET_DESCR_ (sSig, SIGILL, "Попытка выполнить недопустимую операцию. Проверьте указатели на функции.") GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si";
10146 
10147 $ switch (sig)
10148  {
10149  GET_DESCR_ (sSig, SIGSEGV, "Доступ по неверному указателю. Ставьте ассерты!")
10150  GET_DESCR_ (sSig, SIGILL, "Попыткавыполнить недопустимую операцию. Проверьте указатели на функции.") GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si выполнитьнедопустимую операцию. Проверьте указатели на функции.") GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si недопустимую операцию. Проверьтеуказатели на функции.") GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si указателина функции.") GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si на функции") GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10151  GET_DESCR_ (sSig, SIGABRT, "Аварийноезавершение программы, вызвана функция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si завершение программы вызвана функция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si, вызванафункция abort().") GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si функция abort().")
10152  GET_DESCR_ (sSig, SIGTERM, "Полученсигнал принудительного завершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si сигнал принудительногозавершения программы.") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si завершения программы") GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10153  GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка ввычислениях.") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si вычислениях") default: break; } $ const char* sFPE = ""; #if defined (_MSC_VER) // MSVC provides the FPE code as a MS extension. // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx $ if (sig == SIGFPE) switch (fpe) { GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.") GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.") GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10154  default: break;
10155  }
10156 
10157 $ const char* sFPE = "";
10158 
10159  #if defined (_MSC_VER)
10160 
10161  // MSVC provides the FPE code as a MS extension.
10162  // See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
10163 
10164 $ if (sig == SIGFPE) switch (fpe)
10165  {
10166  GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
10167  GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
10168  GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Делениена ноль.") GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si на ноль.")
10169  GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результатслишком большой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si слишкомбольшой.") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si большой") GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10170  GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результатслишком маленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si слишкоммаленький.") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si маленький") GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10171  GET_DESCR_ (sFPE, _FPE_INEXACT, "Результатнеточен.") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si неточен") GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.") GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10172  GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
10173  GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа") GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10174  GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стекасопроцессора.") GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si сопроцессора.")
10175  GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "Встеке сопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si стекесопроцессора не хватает данных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si сопроцессора не хватаетданных.") GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si данных.")
10176  GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явныйвызов исключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si вызовисключения.") default: break; } #else $ fpe = 0; #endif #undef GET_DESCR_ $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal); $ Win32::_fpreset(); $ _TX_UNEXPECTED ("\a\t" "signal (%d, 0x%02X):%s%s " "%s%s" "С помощью функции signal() вы можете сами обработать эту ошибку.", sig, (unsigned) fpe, sSig, sFPE, ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnTerminate() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc $1 static int terminating = 0; if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; } $ if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("\t\a" "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, " "или другая фатальная ошибка C++. " "%s" "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, " "разбирайтесь, в чем дело.\n\n" "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE, _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- void _txOnUnexpected() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 if (!*_txDumpSE) {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); } $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n" "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете " "спецификацию исключений для функций, проверьте, не нарушена ли она." "%s" "С помощью catch (...) в main() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } //----------------------------------------------------------------------------------------------------------------- int _txOnMatherr (_exception* exc) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc); $1 assert (exc); const char* sType = "Неизвестный тип исключения"; #if !defined (__CYGWIN__) #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; } $ switch (exc->type) { GET_DESCR_ (_DOMAIN, "Нарушение области определения"); GET_DESCR_ (_SING, "Сингулярность аргумента"); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si исключения.")
10177  default: break;
10178  }
10179 
10180  #else
10181 $ fpe = 0;
10182  #endif
10183 
10184  #undef GET_DESCR_
10185 
10186 $ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
10187 
10188 $ Win32::_fpreset();
10189 
10190 $ _TX_UNEXPECTED ("\a\t"
10191  "signal (%d, 0x%02X):%s%s "
10192  "%s%s"
10193  "С помощью функции signal() вы можете сами обработать эту ошибку.",
10194  sig, (unsigned) fpe, sSig, sFPE,
10195  ((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
10196  }
10197 
10198 //-----------------------------------------------------------------------------------------------------------------
10199 
10200 void _txOnTerminate()
10201  {
10202  txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
10203 
10204  // From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
10205 
10206 $1 static int terminating = 0;
10207  if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
10208 
10209 $ if (!*_txDumpSE)
10210  {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
10211 
10212 $ _TX_UNEXPECTED ("\t\a"
10213  "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
10214  "или другая фатальная ошибка C++. "
10215  "%s"
10216  "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
10217  "разбирайтесь, в чем дело.\n\n"
10218  "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
10219  _txDumpSE + 1);
10220  }
10221 
10222 //-----------------------------------------------------------------------------------------------------------------
10223 
10224 void _txOnUnexpected()
10225  {
10226  txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
10227 
10228 $1 if (!*_txDumpSE)
10229  {$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
10230 
10231 $ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
10232  "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
10233  "спецификацию исключений для функций, проверьте, не нарушена ли она."
10234  "%s"
10235  "С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
10236  _txDumpSE + 1);
10237  }
10238 
10239 //-----------------------------------------------------------------------------------------------------------------
10240 
10241 int _txOnMatherr (_exception* exc)
10242  {
10243  txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
10244 
10245 $1 assert (exc);
10246 
10247  const char* sType = "Неизвестный тип исключения";
10248 
10249  #if !defined (__CYGWIN__)
10250 
10251  #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
10252 
10253 $ switch (exc->type)
10254  {
10255  GET_DESCR_ (_DOMAIN, "Нарушение области определения");
10256  GET_DESCR_ (_SING, "Сингулярность аргумента); GET_DESCR_ (_PLOSS, "Частичная потеря значимости"); GET_DESCR_ (_TLOSS, "Полная потеря значимости"); GET_DESCR_ (_OVERFLOW, "Результат слишком большой"); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si");
10257  GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
10258  GET_DESCR_ (_TLOSS, "Полная потеря значимости");
10259  GET_DESCR_ (_OVERFLOW, "Результат слишком большой); GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький"); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si");
10260  GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький); default: break; } #undef GET_DESCR_ $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n" "С помощью __setusermatherr() вы можете сами обработать эту ошибку.", exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval); #else $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType); #endif return 0; } //----------------------------------------------------------------------------------------------------------------- void _txOnNewHandlerAnsi() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $1 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n" "С помощью std::set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память."); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code) { txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code); $1 if (code) {$ errno = code; } $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n" "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку " "и постараться не выходить за границы массивов.", code, msg, (char*) ptr, ptr); } //----------------------------------------------------------------------------------------------------------------- inline int tx_glGetError (int setError /*= INT_MIN*/) { $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ MSC Runtime check hooks //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) //----------------------------------------------------------------------------------------------------------------- int _txOnNewHandler (size_t size) { txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size); $5 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n" "С помощью _set_new_handler() вы можете сами обработать эту ошибку " "и где-нибудь найти недостающую память.", (unsigned long long) size); $ throw std::bad_alloc(); } //----------------------------------------------------------------------------------------------------------------- void _txOnSecurityError (int code, void* addr) { txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr); $5 $ _TX_UNEXPECTED ("\a" "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n" "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку " "и более торжественно завершить программу. Ставьте же ассерты.", code); } //----------------------------------------------------------------------------------------------------------------- void _txOnPureCall() { txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__); $5 $ _TX_UNEXPECTED ("\a" "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах " "или деструкторах базовых классов - не вызывайте там таких функций.\n\n" "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку " "и проверить свое знание С++ :)"); } //----------------------------------------------------------------------------------------------------------------- void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr) { txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr); $5 assert (wExpr); assert (wFunc); assert (wFile); char expr [_TX_BUFSIZE/2] = "[Unknowm expr]", func [_TX_BUFSIZE/2] = "[Unknowm func]", file [MAX_PATH] = "[Unknowm file]"; $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL); $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL); $$ _txError (file, (int) line, func, 0, "\a" "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n" "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr); } //----------------------------------------------------------------------------------------------------------------- #if defined (_CLANG_VER) && !defined (_MSC_VER) void _txLibCppDebugFunction (std::__libcpp_debug_info const& info) { $5 assert (&info); $$ _txError (info.__file_, info.__line_, NULL, 0, "\a" "Оказалось неверно, что %s (%s). Не надо так.\n\n" "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_); } #endif //----------------------------------------------------------------------------------------------------------------- #pragma runtime_checks ("", off) int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...) { txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format); $5 static long running = 0; $ while (InterlockedExchange (&running, 1)) Sleep (0); $ assert (format); // Disable all RTC failures $ int nErrors = _RTC_NumErrors(); $ int* errors = NULL; $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;} $ int err = 0; $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE); $ char text [_TX_BUFSIZE] = ""; $ va_list arg; va_start (arg, format); $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number $ va_end (arg); $ const char* sType = "type"; $ switch (type) { case _CRT_ERROR: $ sType = "ошибка"; break; case _CRT_ASSERT: $ sType = "логическая ошибка"; break; case _CRT_WARN: $ sType = "возможная ошибка"; break; default: $ break; } $ const char* sError = _RTC_GetErrDesc (error); $$ _txError (file, line, NULL, 0, "\a" "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module); // The code below will be never executed until the error above will stay fatal: // Restore the RTC error types #if defined (_MSC_VER) #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR)); #if defined (_MSC_VER) #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read. #endif $ InterlockedExchange (&running, 0); $ return 1; } #pragma runtime_checks ("", restore) //----------------------------------------------------------------------------------------------------------------- int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line) { #if (_TX_ALLOW_TRACE +0 >= 4) static _tx_thread int recursive = 0; if (recursive) return true; recursive++; #if (_TX_ALLOW_TRACE +0 <= 10) if (!size) return true; #endif #define GET_DESCR_(str, type) case (type): { str = #type; break; } const char* sType = "Unknown type"; const char* sUse = "Unknown use"; switch (_BLOCK_TYPE (type)) { GET_DESCR_ (sType, _HOOK_ALLOC); GET_DESCR_ (sType, _HOOK_REALLOC); GET_DESCR_ (sType, _HOOK_FREE); default: break; } switch (use) { GET_DESCR_ (sUse, _NORMAL_BLOCK); GET_DESCR_ (sUse, _CRT_BLOCK); GET_DESCR_ (sUse, _CLIENT_BLOCK); GET_DESCR_ (sUse, _FREE_BLOCK); GET_DESCR_ (sUse, _IGNORE_BLOCK); default: break; } #undef GET_DESCR_ _txTrace ((const char*) file, line, NULL, "%*s" "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)", 2 * _txLoc::Cur.inTX, "", type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request); recursive--; #else UNREFERENCED_PARAMETER (type); UNREFERENCED_PARAMETER (data); UNREFERENCED_PARAMETER (size); UNREFERENCED_PARAMETER (use); UNREFERENCED_PARAMETER (request); UNREFERENCED_PARAMETER (file); UNREFERENCED_PARAMETER (line); #endif return true; } //----------------------------------------------------------------------------------------------------------------- #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ SEH staff //----------------------------------------------------------------------------------------------------------------- long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler"); _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc) { int inTX = _txLoc::Cur.inTX++; long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter"); if (_txPrevUEFilter) { if (_txSetJmp()) { int inTX2 = _txLoc::Cur.inTX++; ret = _txPrevUEFilter (exc); _txLoc::Cur.inTX = inTX2; } else { $6 _txClearJmp(); _TX_UNEXPECTED ("\t\a" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n" "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE, _txDumpSE + 1); } } _txLoc::Cur.inTX = inTX; return ret; } //----------------------------------------------------------------------------------------------------------------- LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter) { $6 _txPrevUEFilter = filter; return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter; } //----------------------------------------------------------------------------------------------------------------- long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[]) { assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; } assert (exc->ExceptionRecord); assert (func); assert (func[3] == 'V' || func[3] == 'U'); bool unhExc = (func[3] == 'U'); DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0; void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL; if (code == DBG_PRINTEXCEPTION_C || code == DBG_PRINTEXCEPTION_WIDE_C || code == DBG_THREAD_NAME || (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) || (code == RPC_S_CALL_CANCELLED && !unhExc) || (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent())) return EXCEPTION_CONTINUE_SEARCH; _txSENumber++; if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++; OutputDebugString ("\n"); txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n", _TX_VERSION, _txSENumber, func, (unsigned long) code, addr); $6 if (*(unsigned long long*) _txDumpExceptionObjJmp) { $ longjmp (_txDumpExceptionObjJmp, 1); } tx_fpreset(); #if defined (_MSC_VER) if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); } #endif $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord)); $ if (primaryException && exc) { $ unsigned err = GetLastError(); $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord); $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func); $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace); $ static _tx_thread DWORD prevCode = 0; $ static _tx_thread void* prevAddr = NULL; $ if (code != prevCode && addr != prevAddr && !strstr (_txDumpSE, "Объект исключения C++:")) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ SetLastError (err); $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1); $ prevCode = code; $ prevAddr = addr; } $ SetLastError (err); } $ if (_txDumpSE[0] == '\a' || _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 || _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) { #if !defined (_TX_NO_MINIDUMP) $ _txCreateMiniDump (exc); #endif $ _TX_UNEXPECTED ("\a\t" "%s" "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.", _txDumpSE + 1); } $ return EXCEPTION_CONTINUE_SEARCH; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[]) { $6 assert (what); $ assert (size >= 0); assert (exc); if (!exc) {$ return 0; } $ assert (func); $ unsigned code = exc->ExceptionCode; $ void* addr = exc->ExceptionAddress; $ unsigned params = exc->NumberParameters; $ const ULONG_PTR* info = exc->ExceptionInformation; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ char* s = what; #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__) $ const char* sCode = NULL; $ const char* sDescr = NULL; #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; } $ switch (code) { GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.") GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.") GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.") GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si");
10261  default: break;
10262  }
10263 
10264  #undef GET_DESCR_
10265 
10266 $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
10267  "С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
10268  exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
10269  #else
10270 
10271 $ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
10272 
10273  #endif
10274 
10275  return 0;
10276  }
10277 
10278 //-----------------------------------------------------------------------------------------------------------------
10279 
10280 void _txOnNewHandlerAnsi()
10281  {
10282  txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
10283 $1
10284 $ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
10285  "С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
10286  "и где-нибудь найти недостающую память.");
10287 
10288 $ throw std::bad_alloc();
10289  }
10290 
10291 //-----------------------------------------------------------------------------------------------------------------
10292 
10293 void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
10294  {
10295  txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
10296 
10297 $1 if (code)
10298  {$ errno = code; }
10299 
10300 $ _TX_UNEXPECTED ("\a"
10301  "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
10302  "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
10303  "и постараться не выходить за границы массивов.",
10304  code, msg, (char*) ptr, ptr);
10305  }
10306 
10307 //-----------------------------------------------------------------------------------------------------------------
10308 
10309 inline int tx_glGetError (int setError /*= INT_MIN*/)
10310  {
10311 $1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
10312  }
10313 
10314 //}
10315 //-----------------------------------------------------------------------------------------------------------------
10316 
10317 //-----------------------------------------------------------------------------------------------------------------
10318 //{ MSC Runtime check hooks
10319 //-----------------------------------------------------------------------------------------------------------------
10320 
10321 #if defined (_MSC_VER)
10322 
10323 //-----------------------------------------------------------------------------------------------------------------
10324 
10325 int _txOnNewHandler (size_t size)
10326  {
10327  txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
10328 $5
10329 $ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
10330  "С помощью _set_new_handler() вы можете сами обработать эту ошибку "
10331  "и где-нибудь найти недостающую память.", (unsigned long long) size);
10332 
10333 $ throw std::bad_alloc();
10334  }
10335 
10336 //-----------------------------------------------------------------------------------------------------------------
10337 
10338 void _txOnSecurityError (int code, void* addr)
10339  {
10340  txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
10341 $5
10342 $ _TX_UNEXPECTED ("\a"
10343  "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
10344  "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
10345  "и более торжественно завершить программу. Ставьте же ассерты.", code);
10346  }
10347 
10348 //-----------------------------------------------------------------------------------------------------------------
10349 
10350 void _txOnPureCall()
10351  {
10352  txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
10353 $5
10354 $ _TX_UNEXPECTED ("\a"
10355  "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
10356  "или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
10357  "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
10358  "и проверить свое знание С++ :)");
10359  }
10360 
10361 //-----------------------------------------------------------------------------------------------------------------
10362 
10363 void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
10364  {
10365  txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
10366 
10367 $5 assert (wExpr);
10368  assert (wFunc);
10369  assert (wFile);
10370 
10371  char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
10372  func [_TX_BUFSIZE/2] = "[Unknowm func]",
10373  file [MAX_PATH] = "[Unknowm file]";
10374 
10375 $ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
10376 $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
10377 $ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
10378 
10379 $$ _txError (file, (int) line, func, 0, "\a"
10380  "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
10381  "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
10382  }
10383 
10384 //-----------------------------------------------------------------------------------------------------------------
10385 
10386 #if defined (_CLANG_VER) && !defined (_MSC_VER)
10387 
10388 void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
10389  {
10390 $5 assert (&info);
10391 
10392 $$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
10393  "Оказалось неверно, что %s (%s). Не надо так.\n\n"
10394  "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
10395  }
10396 
10397 #endif
10398 
10399 //-----------------------------------------------------------------------------------------------------------------
10400 
10401 #pragma runtime_checks ("", off)
10402 
10403 int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
10404  {
10405  txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
10406 
10407 $5 static long running = 0;
10408 $ while (InterlockedExchange (&running, 1)) Sleep (0);
10409 
10410 $ assert (format);
10411 
10412  // Disable all RTC failures
10413 
10414 $ int nErrors = _RTC_NumErrors();
10415 $ int* errors = NULL;
10416 $ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
10417 
10418 $ int err = 0;
10419 $ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
10420 
10421 $ char text [_TX_BUFSIZE] = "";
10422 
10423 $ va_list arg; va_start (arg, format);
10424 $ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
10425 $ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
10426 $ va_end (arg);
10427 
10428 $ const char* sType = "type";
10429 
10430 $ switch (type)
10431  {
10432  case _CRT_ERROR: $ sType = "ошибка"; break;
10433  case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
10434  case _CRT_WARN: $ sType = "возможная ошибка"; break;
10435  default: $ break;
10436  }
10437 
10438 $ const char* sError = _RTC_GetErrDesc (error);
10439 
10440 $$ _txError (file, line, NULL, 0, "\a"
10441  "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
10442 
10443  // The code below will be never executed until the error above will stay fatal:
10444 
10445  // Restore the RTC error types
10446 
10447  #if defined (_MSC_VER)
10448  #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
10449  #endif
10450 
10451 $ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
10452 
10453  #if defined (_MSC_VER)
10454  #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
10455  #endif
10456 
10457 $ InterlockedExchange (&running, 0);
10458 $ return 1;
10459  }
10460 
10461 #pragma runtime_checks ("", restore)
10462 
10463 //-----------------------------------------------------------------------------------------------------------------
10464 
10465 int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
10466  {
10467  #if (_TX_ALLOW_TRACE +0 >= 4)
10468 
10469  static _tx_thread int recursive = 0;
10470  if (recursive) return true;
10471  recursive++;
10472 
10473  #if (_TX_ALLOW_TRACE +0 <= 10)
10474  if (!size) return true;
10475  #endif
10476 
10477  #define GET_DESCR_(str, type) case (type): { str = #type; break; }
10478 
10479  const char* sType = "Unknown type";
10480  const char* sUse = "Unknown use";
10481 
10482  switch (_BLOCK_TYPE (type))
10483  {
10484  GET_DESCR_ (sType, _HOOK_ALLOC);
10485  GET_DESCR_ (sType, _HOOK_REALLOC);
10486  GET_DESCR_ (sType, _HOOK_FREE);
10487  default: break;
10488  }
10489 
10490  switch (use)
10491  {
10492  GET_DESCR_ (sUse, _NORMAL_BLOCK);
10493  GET_DESCR_ (sUse, _CRT_BLOCK);
10494  GET_DESCR_ (sUse, _CLIENT_BLOCK);
10495  GET_DESCR_ (sUse, _FREE_BLOCK);
10496  GET_DESCR_ (sUse, _IGNORE_BLOCK);
10497  default: break;
10498  }
10499 
10500  #undef GET_DESCR_
10501 
10502  _txTrace ((const char*) file, line, NULL, "%*s"
10503  "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
10504  2 * _txLoc::Cur.inTX, "",
10505  type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
10506 
10507  recursive--;
10508 
10509  #else
10510 
10511  UNREFERENCED_PARAMETER (type);
10512  UNREFERENCED_PARAMETER (data);
10513  UNREFERENCED_PARAMETER (size);
10514  UNREFERENCED_PARAMETER (use);
10515  UNREFERENCED_PARAMETER (request);
10516  UNREFERENCED_PARAMETER (file);
10517  UNREFERENCED_PARAMETER (line);
10518 
10519  #endif
10520 
10521  return true;
10522  }
10523 
10524 //-----------------------------------------------------------------------------------------------------------------
10525 
10526 #endif
10527 
10528 //}
10529 //-----------------------------------------------------------------------------------------------------------------
10530 
10531 //-----------------------------------------------------------------------------------------------------------------
10532 //{ SEH staff
10533 //-----------------------------------------------------------------------------------------------------------------
10534 
10535 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
10536  {
10537  int inTX = _txLoc::Cur.inTX++;
10538 
10539  long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
10540 
10541  _txLoc::Cur.inTX = inTX;
10542  return ret;
10543  }
10544 
10545 //-----------------------------------------------------------------------------------------------------------------
10546 
10547 long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
10548  {
10549  int inTX = _txLoc::Cur.inTX++;
10550 
10551  long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
10552 
10553  if (_txPrevUEFilter)
10554  {
10555  if (_txSetJmp())
10556  {
10557  int inTX2 = _txLoc::Cur.inTX++;
10558 
10559  ret = _txPrevUEFilter (exc);
10560 
10561  _txLoc::Cur.inTX = inTX2;
10562  }
10563  else
10564  {
10565 $6 _txClearJmp();
10566 
10567  _TX_UNEXPECTED ("\t\a" "%s"
10568  "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
10569  "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
10570  _txDumpSE + 1);
10571  }
10572  }
10573 
10574  _txLoc::Cur.inTX = inTX;
10575  return ret;
10576  }
10577 
10578 //-----------------------------------------------------------------------------------------------------------------
10579 
10580 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
10581  {
10582 $6 _txPrevUEFilter = filter;
10583 
10584  return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
10585  }
10586 
10587 //-----------------------------------------------------------------------------------------------------------------
10588 
10589 long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
10590  {
10591  assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
10592 
10593  assert (exc->ExceptionRecord);
10594 
10595  assert (func);
10596  assert (func[3] == 'V' || func[3] == 'U');
10597 
10598  bool unhExc = (func[3] == 'U');
10599  DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
10600  void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
10601 
10602  if (code == DBG_PRINTEXCEPTION_C ||
10603  code == DBG_PRINTEXCEPTION_WIDE_C ||
10604  code == DBG_THREAD_NAME ||
10605  (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
10606  (code == RPC_S_CALL_CANCELLED && !unhExc) ||
10607  (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
10608  return EXCEPTION_CONTINUE_SEARCH;
10609 
10610  _txSENumber++;
10611  if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
10612 
10613  OutputDebugString ("\n");
10614  txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
10615  _TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
10616 
10617 $6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
10618  {
10619 $ longjmp (_txDumpExceptionObjJmp, 1);
10620  }
10621 
10622  tx_fpreset();
10623 
10624  #if defined (_MSC_VER)
10625  if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
10626  #endif
10627 
10628 $ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
10629 
10630 $ if (primaryException && exc)
10631  {
10632 $ unsigned err = GetLastError();
10633 
10634 $ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
10635 
10636 $ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
10637 $ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
10638 
10639 $ static _tx_thread DWORD prevCode = 0;
10640 $ static _tx_thread void* prevAddr = NULL;
10641 
10642 $ if (code != prevCode && addr != prevAddr &&
10643  !strstr (_txDumpSE, "Объект исключения C++:"))
10644  {
10645  #if !defined (_TX_NO_MINIDUMP)
10646 $ _txCreateMiniDump (exc);
10647  #endif
10648 
10649 $ SetLastError (err);
10650 $ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
10651 
10652 $ prevCode = code;
10653 $ prevAddr = addr;
10654  }
10655 
10656 $ SetLastError (err);
10657  }
10658 
10659 $ if (_txDumpSE[0] == '\a' ||
10660  _txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
10661  _txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
10662  {
10663  #if !defined (_TX_NO_MINIDUMP)
10664 $ _txCreateMiniDump (exc);
10665  #endif
10666 
10667 $ _TX_UNEXPECTED ("\a\t" "%s"
10668  "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
10669  _txDumpSE + 1);
10670  }
10671 
10672 $ return EXCEPTION_CONTINUE_SEARCH;
10673  }
10674 
10675 //-----------------------------------------------------------------------------------------------------------------
10676 
10677 intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
10678  {
10679 $6 assert (what);
10680 $ assert (size >= 0);
10681  assert (exc); if (!exc) {$ return 0; }
10682 $ assert (func);
10683 
10684 $ unsigned code = exc->ExceptionCode;
10685 $ void* addr = exc->ExceptionAddress;
10686 $ unsigned params = exc->NumberParameters;
10687 $ const ULONG_PTR* info = exc->ExceptionInformation;
10688 $ void* object = (params >= 2)? (void*) info[1] : NULL;
10689 
10690 $ char* s = what;
10691 
10692  #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
10693 
10694 $ const char* sCode = NULL;
10695 $ const char* sDescr = NULL;
10696 
10697  #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
10698 
10699 $ switch (code)
10700  {
10701  GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
10702  GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
10703  GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
10704  GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выходза границы массива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si за границымассива. Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si массива Ставьте ассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si. Ставьтеассерты!") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si ассерты") GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si!")
10705  GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точкаостанова. Удачи в отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si останова. Удачив отладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si вотладке!") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si отладке") GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si!")
10706  GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушениевыравнивания данных.") GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si выравнивания данных.")
10707  GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратилневерное значение.") GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si неверное значение.")
10708  GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузитьнужную страницу памяти.") GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si нужную страницу памяти.")
10709  GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжениевыполнения невозможно.") GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si выполнения невозможно.")
10710  GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполненаинструкция машинного кода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si инструкция машинногокода. Одна штука.") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si кода. Одна штука") GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10711  GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Юху! Переполнение стека!") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si-ху! Переполнение стека") GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si!")
10712  GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попыткадоступа к защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si доступак защищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si кзащищенной странице памяти.") GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si защищенной странице памяти.")
10713  GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный илиуже закрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si ужезакрытый дескриптор.") GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.") GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si закрытый дескриптор.")
10714  GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
10715 
10716  GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стекапри операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si приоперации с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si операции сплавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si плавающейточкой.") GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10717  GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числас плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si сплавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si плавающейточкой.") GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10718  GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Делениена ноль при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si на ноль приоперации с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si операции сплавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si плавающейточкой.") GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10719  GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результатпри операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si приоперации с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si операции сплавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si плавающейточкой.") GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10720  GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция сплавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si плавающейточкой.") GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10721  GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение приоперации с плавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si операции сплавающей точкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si плавающейточкой.") GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10722  GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости приоперации с плавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si операции сплавающей точкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si плавающейточкой.") GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10723  GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественныеошибки с плавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si ошибки сплавающей точкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si плавающейточкой.") GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10724 
10725  GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленноеделение на ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si делениена ноль.") GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si на ноль.")
10726  GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение прицелочисленной операции.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si целочисленнойоперации.") GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si операции.")
10727 
10728  GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой средыисполнения (CLR).") GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si исполнения (CLR).")
10729  GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стековогобуфера!") GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si буфера!")
10730  GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот разиз ядра.") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si из ядра") GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10731  GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точкаостанова подсистемы эмуляции x86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si останова подсистемы эмуляцииx86.") GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si x86.")
10732  GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестныйинтерфейс удаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si интерфейсудаленного вызова процедур (RPC).") GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.") GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si удаленного вызова процедур (RPC).")
10733  GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
10734  GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил потоксознания.") GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si сознания.")
10735  GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс") GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10736  GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получилсигнал прерывания Control+C.") GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si сигнал прерывания Control+C.")
10737  GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получилсигнал прерывания Control+Break.") GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si сигнал прерывания Control+Break.")
10738  GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получилуказание дать потоку имя.") GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si указание дать потоку имя.")
10739  GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывелисключение по CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si исключениепо CTRL+C (OutputDebugStringA).") GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si по CTRL+C (OutputDebugStringA).")
10740  GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывелисключение по CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si исключениепо CTRL+C (OutputDebugStringW).") GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si по CTRL+C (OutputDebugStringW).")
10741 
10742  GET_DESCR_ (EXCEPTION_CPP_MSC, " " "ИсключениеС++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si С+, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si++, вызванноеоператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si оператором throw.")
10743  GET_DESCR_ (EXCEPTION_CPP_GCC, " " "ИсключениеС++, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si С+, вызванное оператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si++, вызванноеоператором throw.") GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si оператором throw.")
10744  GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "ИсключениеС++, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si С+, вызванное во время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si++, вызванноево время раскрутки стека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si во время раскруткистека (rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si стека(rethrow?).") GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si (rethrow?).")
10745  GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "ИсключениеС++, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si С+, вызванное нарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si++, вызванноенарушением магии.") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si нарушением магии") GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si.")
10746  GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Этоскомпилилось под Билдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si скомпилилось подБилдер? O_O") GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si Билдер? O_O")
10747  GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Этоже С++. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si же С+. Как это вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si++. Какэто вышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si этовышло?") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si вышло") default: $ break; } #undef GET_DESCR_ $ if (sDescr) { $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode); } else { $ PRINT_ ("\a#%ld: ", _txSENumber); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ ("\r\r"); } $ PRINT_ (" (0x%X) при выполнении кода по адресу", code); $ PRINT_ ((addr? " 0x%p" : " NULL"), addr); $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); } $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name); $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber); $ if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (". Попытка "); $ unsigned long op = 0xBADC0DE; $ const char* sOp = "(действие не указано)"; $ if (params >= 1) { $ switch (op = (unsigned long) info[0]) { case 0: $ sOp = "прочесть данные"; break; case 1: $ sOp = "записать данные"; break; case 8: $ sOp = "исполнить код"; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si?")
10748 
10749  default: $ break;
10750  }
10751 
10752  #undef GET_DESCR_
10753 
10754 $ if (sDescr)
10755  {
10756 $ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
10757  }
10758  else
10759  {
10760 $ PRINT_ ("\a#%ld: ", _txSENumber);
10761 $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
10762  GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
10763  s, (DWORD) (size - (s-what)), NULL) - 2;
10764 $ PRINT_ ("\r\r");
10765  }
10766 
10767 $ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
10768 $ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
10769 
10770 $ Win32::SYMBOL_INFO* sym = NULL;
10771 $ Win32::IMAGEHLP_LINE64* line = NULL;
10772 
10773  if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
10774 
10775 $ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
10776 $ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
10777 
10778 $ if (code == EXCEPTION_ACCESS_VIOLATION ||
10779  code == EXCEPTION_IN_PAGE_ERROR)
10780  {
10781 $ PRINT_ (". Попытка ");
10782 
10783 $ unsigned long op = 0xBADC0DE;
10784 $ const char* sOp = "(действие не указано)";
10785 
10786 $ if (params >= 1)
10787  {
10788 $ switch (op = (unsigned long) info[0])
10789  {
10790  case 0: $ sOp = "прочесть данные"; break;
10791  case 1: $ sOp = "записать данные"; break;
10792  case 8: $ sOp = "исполнить код; break; default: $ sOp = "совершить операцию 0x%lX"; break; } } $ PRINT_ (sOp, op); $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); } else {$ PRINT_ (" (адрес не указан)"); } $ if (code == EXCEPTION_IN_PAGE_ERROR) { $ PRINT_ (", ошибка ввода-вывода:"); $ if (params >= 3) { $ unsigned long ntstatus = (unsigned long) info[2]; $ PRINT_ (" 0x%lX (", ntstatus); $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (size - (s-what)), NULL) - 2; $ PRINT_ (")"); } else {$ PRINT_ (" (не указана)"); } } } $ HMODULE module = NULL; $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module)); $ if (module) { $ static char sModule [MAX_PATH] = ""; $ int ok = GetModuleFileName (module, sModule, sizeof (sModule)); $ char* ext = (ok? strrchr (sModule, '.') : NULL); $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule)); if (ok) {$ PRINT_ (" в модуле %s", sModule); } else {$ PRINT_ (" в модуле 0x%p", module); } } $ PRINT_ ("."); $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); } $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0) {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); } $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func); $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE) {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); } $ if (exc->ExceptionRecord) { $ PRINT_ ("\n\n" "Причина:" "\n\n"); $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func); } $ if (code == EXCEPTION_CPP_GCC || code == EXCEPTION_CPP_GCC_UNWIND || code == EXCEPTION_CPP_GCC_FORCED || code == EXCEPTION_CPP_MSC) { $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info); } #undef PRINT_ $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n"); $ return s - what; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionCPP (char what[], intptr_t size, unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/) { $6 assert (what); $ assert (size >= 0); $ char* s = what; $ switch (code) { #if defined (_GCC_VER) case EXCEPTION_CPP_GCC: case EXCEPTION_CPP_GCC_UNWIND: case EXCEPTION_CPP_GCC_FORCED: { // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below // and figure above near ABI::__cxa_exception definition in this file $ const std::type_info* type = NULL; $ void* object = NULL; $ if (params >= 1) { $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0]; $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1; $ type = cxa_exception->exceptionType; $ object = cxa_exception + 1; } $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type); } $ break; case 0: // Not called within SEH chain { // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc using namespace abi; $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions; $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above { $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1; } $ if (cxa_exception) { $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type()); $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType); } } $ break; #elif defined (_MSC_VER) case EXCEPTION_CPP_MSC: { // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 // [2] http://www.openrce.org/articles/full_view/21 // [3] http://www.openrce.org/articles/full_view/23 // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf $ const std::type_info* type = NULL; $ void* object = (params >= 2)? (void*) info[1] : NULL; $ size_t szObj = 0; $ if (params >= 3 && (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 || info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1)) { $ auto throwInfo = (const Win32::ThrowInfo*) info[2]; $ if (throwInfo && throwInfo->pCatchableTypeArray) { $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL; #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) ) $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray); $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]); $ type = RVA_(const std::type_info*, cType->pType); $ szObj = cType->sizeOrOffset; #undef RVA_ } } $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type); } break; case 0: // Not called within SEH chain // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC: // // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp // // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception. // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp // and http://msdn.microsoft.com/en-us/library/ff730818.aspx. // // So _txDumpSE information should have been recorded during previous call. Now do nothing. $ break; #endif default: $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code); $ break; } $ while (s > what && s[-1] == '\n') s--; $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n"); $ return (s - what); } //----------------------------------------------------------------------------------------------------------------- intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type) { $6 assert (what); $ assert (size > 0); $ static char* s = NULL; s = what; $ static size_t szObj = 0; szObj = sizeObj; #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__) $ PRINT_ ("\n\n" "Объект исключения C++:"); $ const char* mangledName = (type)? type->name() : NULL; $ char* typeName = NULL; $ int err = 1; #if defined (_GCC_VER) $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); #endif $ const char* name = (!err && typeName)? typeName : mangledName; $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0)) {$ name = "std::string"; } $ if (name && (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 || strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 || strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0)) {$ name = "std::string*"; } if (name) {$ PRINT_ (" %s", name); } #if defined (_GCC_VER) $ free (typeName); #endif $ err = 0; $ if (mangledName) { if (_txSetJmp()) { #define PRINT_VAL_(fmt, typ, ...) \ else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \ else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } if (false) ; PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false") PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float) PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double) PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char) PRINT_VAL_ ("%s", std::string, .c_str()) else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object)) { $ PRINT_ (", what(): \"%s\"", e->what()); } else {$ err = 1; } } else {$ err = 2; } } $ _txClearJmp(); $ if (err && object && szObj) { $ const unsigned char* buf = (const unsigned char*) object; $ if (szObj >= 64) szObj = 64; $ PRINT_ (", дамп: ["); $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' ); $ PRINT_ ("]"); $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]); $ err = 0; } $ if (err) {$ PRINT_ (" = ???"); } $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object); #undef PRINT_VAL_ #undef PRINT_ $ return s - what; } //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Stack trace and debug info access //----------------------------------------------------------------------------------------------------------------- const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/, CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/) { $6 const int maxFrames = 62; // MS says: < 63 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = ""; if (framesToSkip == -1) {$ return trace; } $ static void* capture [maxFrames] = {}; $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread); $ memset (trace, 0, sizeof (trace)); $ char* s = trace; #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__) $ for (int i = 0, n = 0; i < frames; i++) { $ void* addr = capture[i]; $ Win32::SYMBOL_INFO* sym = NULL; $ Win32::IMAGEHLP_LINE64* line = NULL; $ const char* module = NULL; $ const char* source = NULL; $ bool inTX = false; if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); } if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); } $ int nl = 0; $ while (s > trace && s[-1] == '\n') { s--; nl++; } #if !defined (_TX_FULL_STACKTRACE) $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber)))) {$ continue; } #endif $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr); $ n++; if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; } if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; } if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; } if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); } if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); } if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); } if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); } if (source) {$ PRINT_ (":\n\n" "%s\n", source); } if (sym && strcmp (sym->Name , "main") == 0) {$ break; } } #if defined (_MSC_VER) #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s' #endif $ while (s > trace && s[-1] == '\n') s--; $ *s = 0; #if defined (_MSC_VER) #pragma warning (default: 28199) // Using possibly uninitialized memory '*s' #endif #undef PRINT_ $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), ""); $ return trace; } //----------------------------------------------------------------------------------------------------------------- // Stack WALKING if the program is DEAD. Dead, Carl! int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/, CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/) { $6 namespace MinGW = Win32::MinGW; $ assert (capture); $ int cpu = 0; $ Win32::STACKFRAME64 frame = {}; $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat; $ CONTEXT ctx = {}; $ ctx.ContextFlags |= CONTEXT_FULL; $ int isWow64 = 0; $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64); else {$ return -1; } $ if (context) { $ ctx = *context; } else { $ assert (Win32::RtlCaptureContext); $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx)); } #if defined (_WIN64) $ if (isWow64) { $ Win32::WOW64_CONTEXT wow64ctx = {}; $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL; $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT {$ return 0; } $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = wow64ctx.Eip; $ frame.AddrStack.Offset = wow64ctx.Esp; $ frame.AddrFrame.Offset = wow64ctx.Ebp; } else { $ cpu = IMAGE_FILE_MACHINE_AMD64; $ frame.AddrPC .Offset = ctx.Rip; $ frame.AddrStack.Offset = ctx.Rbp; $ frame.AddrFrame.Offset = ctx.Rsp; } #else { $ cpu = IMAGE_FILE_MACHINE_I386; $ frame.AddrPC .Offset = ctx.Eip; $ frame.AddrStack.Offset = ctx.Ebp; $ frame.AddrFrame.Offset = ctx.Esp; } #endif $ assert (cpu); if (_txSetJmp()) { $ _txSymGetFromAddr ((void*) 1); } $ _txClearJmp(); $ HANDLE process = GetCurrentProcess(); $ int frames = 0; $ for (frames = -framesToSkip; frames < (int) szCapture; frames++) { $ DWORD64 prev = frame.AddrStack.Offset; // Я злой и страшный серый walk. Я в поросятах знаю talk. if (!_txSetJmp()) {$ break; } #if defined (_GCC_VER) if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; } #elif defined (_MSC_VER) $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL, Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; } #else #error _GCC_VER / _MSC_VER not defined #endif if (frames < 0) {$ continue; } $ void* addr = (void*) frame.AddrPC.Offset; if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame $ assert (0 <= frames && frames < (int) szCapture); $ capture[frames] = addr; } $ _txClearJmp(); $ return frames; } // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl //----------------------------------------------------------------------------------------------------------------- bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/, Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/, const char** source /*= NULL*/, int context /*= 2*/) { $7 static HANDLE process = NULL; #if defined (_GCC_VER) #define LIB_ Win32::MinGW #elif defined (_MSC_VER) #define LIB_ Win32 #else #error _GCC_VER / _MSC_VER not defined #endif $ if (!process && addr) { $ process = GetCurrentProcess(); $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS; $ _TX_CALL (LIB_::SymSetOptions, (options)); $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true)); } $ static DWORD64 mod = 0; $ if (module) { $ static char sMod [MAX_PATH] = ""; $ memset (sMod, 0, sizeof (sMod)); $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr)); $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH); $ char* ext = strrchr (sMod, '.'); if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); } $ *module = sMod; } $ static char buffer [_TX_BUFSIZE] = ""; $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer; $ if (symbol) { $ memset (buffer, 0, sizeof (buffer)); $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1; $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO); $ unsigned long long ofs = 0; $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym)); if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; } $ *symbol = sym; } $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) }; $ if (line) { $ memset (&line64, 0, sizeof (line64)); $ DWORD ofs = 0; $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64)); $ *line = &line64; } $ if (source) { $ static char buf [_TX_BUFSIZE] = ""; $ memset (buf, 0, sizeof (buf)); $ if (line64.FileName && line64.LineNumber) { $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName, (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber); $ *source = buf; } if (!*source || !**source) {$ *source = NULL; } } $ if (!addr && process) { $ _TX_CALL (LIB_::SymCleanup, (process)); $ process = NULL; } $ if (symbol) { $ if (strstr (sym->Name, "::TX::") || (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) || (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) || strncmp (sym->Name, "_tx_", 4) == 0 || strncmp (sym->Name, "tx_", 3) == 0) { $ return true; } $ if (!line || !line64.FileName) return false; $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1); $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) && (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\'); } #undef LIB_ $ return false; } //----------------------------------------------------------------------------------------------------------------- intptr_t _txReadSource (char buf[], intptr_t si"; break;
10793  default: $ sOp = "совершить операцию 0x%lX"; break;
10794  }
10795  }
10796 
10797 $ PRINT_ (sOp, op);
10798 
10799 $ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
10800  else {$ PRINT_ (" (адрес не указан)"); }
10801 
10802 $ if (code == EXCEPTION_IN_PAGE_ERROR)
10803  {
10804 $ PRINT_ (", ошибка ввода-вывода:");
10805 
10806 $ if (params >= 3)
10807  {
10808 $ unsigned long ntstatus = (unsigned long) info[2];
10809 
10810 $ PRINT_ (" 0x%lX (", ntstatus);
10811 
10812 $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
10813  GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
10814  s, (DWORD) (size - (s-what)), NULL) - 2;
10815 $ PRINT_ (")");
10816  }
10817  else
10818  {$ PRINT_ (" (не указана)"); }
10819  }
10820  }
10821 
10822 $ HMODULE module = NULL;
10823 $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
10824 
10825 $ if (module)
10826  {
10827 $ static char sModule [MAX_PATH] = "";
10828 $ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
10829 
10830 $ char* ext = (ok? strrchr (sModule, '.') : NULL);
10831 $ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
10832 
10833  if (ok) {$ PRINT_ (" в модуле %s", sModule); }
10834  else {$ PRINT_ (" в модуле 0x%p", module); }
10835  }
10836 
10837 $ PRINT_ (".");
10838 
10839 $ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
10840  {$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
10841 
10842 $ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
10843  {$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
10844 
10845 $ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
10846 
10847 $ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
10848  {$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
10849 
10850 $ if (exc->ExceptionRecord)
10851  {
10852 $ PRINT_ ("\n\n" "Причина:" "\n\n");
10853 $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
10854  }
10855 
10856 $ if (code == EXCEPTION_CPP_GCC ||
10857  code == EXCEPTION_CPP_GCC_UNWIND ||
10858  code == EXCEPTION_CPP_GCC_FORCED ||
10859  code == EXCEPTION_CPP_MSC)
10860  {
10861 $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
10862  }
10863 
10864  #undef PRINT_
10865 
10866 $ while (s > what && s[-1] == '\n') s--;
10867 $ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
10868 
10869 $ return s - what;
10870  }
10871 
10872 //-----------------------------------------------------------------------------------------------------------------
10873 
10874 intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
10875  unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
10876  {
10877 $6 assert (what);
10878 $ assert (size >= 0);
10879 
10880 $ char* s = what;
10881 
10882 $ switch (code)
10883  {
10884  #if defined (_GCC_VER)
10885 
10886  case EXCEPTION_CPP_GCC:
10887  case EXCEPTION_CPP_GCC_UNWIND:
10888  case EXCEPTION_CPP_GCC_FORCED:
10889  {
10890  // See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
10891  // [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
10892  // [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
10893  // [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
10894  // and figure above near ABI::__cxa_exception definition in this file
10895 
10896 $ const std::type_info* type = NULL;
10897 $ void* object = NULL;
10898 
10899 $ if (params >= 1)
10900  {
10901 $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
10902 $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
10903 
10904 $ type = cxa_exception->exceptionType;
10905 $ object = cxa_exception + 1;
10906  }
10907 
10908 $ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
10909  }
10910 $ break;
10911 
10912  case 0: // Not called within SEH chain
10913  {
10914  // From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
10915  // [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
10916 
10917  using namespace abi;
10918 
10919 $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
10920 
10921 $ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
10922  {
10923 $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
10924  }
10925 
10926 $ if (cxa_exception)
10927  {
10928 $ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
10929 
10930 $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
10931  }
10932  }
10933 $ break;
10934 
10935  #elif defined (_MSC_VER)
10936 
10937  case EXCEPTION_CPP_MSC:
10938  {
10939  // See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
10940  // [2] http://www.openrce.org/articles/full_view/21
10941  // [3] http://www.openrce.org/articles/full_view/23
10942  // [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
10943 
10944 $ const std::type_info* type = NULL;
10945 $ void* object = (params >= 2)? (void*) info[1] : NULL;
10946 $ size_t szObj = 0;
10947 
10948 $ if (params >= 3 &&
10949  (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
10950  info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
10951  info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
10952  info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
10953  {
10954 $ auto throwInfo = (const Win32::ThrowInfo*) info[2];
10955 
10956 $ if (throwInfo && throwInfo->pCatchableTypeArray)
10957  {
10958 $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
10959 
10960  #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
10961 
10962 $ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
10963 $ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
10964 
10965 $ type = RVA_(const std::type_info*, cType->pType);
10966 $ szObj = cType->sizeOrOffset;
10967 
10968  #undef RVA_
10969  }
10970  }
10971 
10972 $ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
10973  }
10974  break;
10975 
10976  case 0: // Not called within SEH chain
10977 
10978  // signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
10979  //
10980  // terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
10981  // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
10982  //
10983  // signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
10984  // See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
10985  // and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
10986  // and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
10987  //
10988  // So _txDumpSE information should have been recorded during previous call. Now do nothing.
10989 
10990 $ break;
10991 
10992  #endif
10993 
10994  default:
10995 $ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
10996 $ break;
10997  }
10998 
10999 $ while (s > what && s[-1] == '\n') s--;
11000 $ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
11001 
11002 $ return (s - what);
11003  }
11004 
11005 //-----------------------------------------------------------------------------------------------------------------
11006 
11007 intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
11008  {
11009 $6 assert (what);
11010 $ assert (size > 0);
11011 
11012 $ static char* s = NULL; s = what;
11013 $ static size_t szObj = 0; szObj = sizeObj;
11014 
11015  #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
11016 
11017 $ PRINT_ ("\n\n" "Объект исключения C++:");
11018 
11019 $ const char* mangledName = (type)? type->name() : NULL;
11020 
11021 $ char* typeName = NULL;
11022 $ int err = 1;
11023 
11024  #if defined (_GCC_VER)
11025 $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
11026  #endif
11027 
11028 $ const char* name = (!err && typeName)? typeName : mangledName;
11029 
11030 $ if (name &&
11031  (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
11032  strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
11033  {$ name = "std::string"; }
11034 
11035 $ if (name &&
11036  (strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
11037  strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
11038  strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
11039  {$ name = "std::string*"; }
11040 
11041  if (name) {$ PRINT_ (" %s", name); }
11042 
11043  #if defined (_GCC_VER)
11044 $ free (typeName);
11045  #endif
11046 
11047 $ err = 0;
11048 $ if (mangledName)
11049  {
11050  if (_txSetJmp())
11051  {
11052  #define PRINT_VAL_(fmt, typ, ...) \
11053  else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
11054  else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
11055  else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
11056  else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
11057  else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
11058  else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
11059 
11060  if (false) ;
11061  PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
11062  PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
11063  PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
11064  PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
11065  PRINT_VAL_ ("%s", std::string, .c_str())
11066 
11067  else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
11068  {
11069 $ PRINT_ (", what(): \"%s\"", e->what());
11070  }
11071  else
11072  {$ err = 1; }
11073  }
11074  else
11075  {$ err = 2; }
11076  }
11077 
11078 $ _txClearJmp();
11079 
11080 $ if (err && object && szObj)
11081  {
11082 $ const unsigned char* buf = (const unsigned char*) object;
11083 
11084 $ if (szObj >= 64) szObj = 64;
11085 
11086 $ PRINT_ (", дамп: [");
11087 $ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
11088 
11089 $ PRINT_ ("]");
11090 $ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
11091 
11092 $ err = 0;
11093  }
11094 
11095 $ if (err)
11096  {$ PRINT_ (" = ???"); }
11097 
11098 $ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
11099 
11100  #undef PRINT_VAL_
11101  #undef PRINT_
11102 
11103 $ return s - what;
11104  }
11105 
11106 //}
11107 //-----------------------------------------------------------------------------------------------------------------
11108 
11109 //-----------------------------------------------------------------------------------------------------------------
11110 //{ Stack trace and debug info access
11111 //-----------------------------------------------------------------------------------------------------------------
11112 
11113 const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
11114  CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
11115  {
11116 $6 const int maxFrames = 62; // MS says: < 63
11117 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
11118 
11119  if (framesToSkip == -1) {$ return trace; }
11120 
11121 $ static void* capture [maxFrames] = {};
11122 $ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
11123 
11124 $ memset (trace, 0, sizeof (trace));
11125 $ char* s = trace;
11126 
11127  #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
11128 
11129 $ for (int i = 0, n = 0; i < frames; i++)
11130  {
11131 $ void* addr = capture[i];
11132 
11133 $ Win32::SYMBOL_INFO* sym = NULL;
11134 $ Win32::IMAGEHLP_LINE64* line = NULL;
11135 $ const char* module = NULL;
11136 $ const char* source = NULL;
11137 $ bool inTX = false;
11138 
11139  if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
11140  if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
11141 
11142 $ int nl = 0;
11143 $ while (s > trace && s[-1] == '\n') { s--; nl++; }
11144 
11145  #if !defined (_TX_FULL_STACKTRACE)
11146 
11147 $ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
11148  {$ continue; }
11149 
11150  #endif
11151 
11152 $ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
11153 $ n++;
11154 
11155  if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
11156  if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
11157  if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
11158 
11159  if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
11160  if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
11161  if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
11162  if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
11163  if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
11164 
11165  if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
11166  }
11167 
11168  #if defined (_MSC_VER)
11169  #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
11170  #endif
11171 
11172 $ while (s > trace && s[-1] == '\n') s--;
11173 $ *s = 0;
11174 
11175  #if defined (_MSC_VER)
11176  #pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
11177  #endif
11178 
11179  #undef PRINT_
11180 
11181 $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
11182 
11183 $ return trace;
11184  }
11185 
11186 //-----------------------------------------------------------------------------------------------------------------
11187 
11188 // Stack WALKING if the program is DEAD. Dead, Carl!
11189 
11190 int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
11191  CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
11192  {
11193 $6 namespace MinGW = Win32::MinGW;
11194 
11195 $ assert (capture);
11196 
11197 $ int cpu = 0;
11198 
11199 $ Win32::STACKFRAME64 frame = {};
11200 $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
11201 
11202 $ CONTEXT ctx = {};
11203 $ ctx.ContextFlags |= CONTEXT_FULL;
11204 
11205 $ int isWow64 = 0;
11206 $ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
11207  else {$ return -1; }
11208 
11209 $ if (context)
11210  {
11211 $ ctx = *context;
11212  }
11213  else
11214  {
11215 $ assert (Win32::RtlCaptureContext);
11216 $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
11217  }
11218 
11219  #if defined (_WIN64)
11220 
11221 $ if (isWow64)
11222  {
11223 $ Win32::WOW64_CONTEXT wow64ctx = {};
11224 $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
11225 
11226 $ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
11227  {$ return 0; }
11228 
11229 $ cpu = IMAGE_FILE_MACHINE_I386;
11230 
11231 $ frame.AddrPC .Offset = wow64ctx.Eip;
11232 $ frame.AddrStack.Offset = wow64ctx.Esp;
11233 $ frame.AddrFrame.Offset = wow64ctx.Ebp;
11234  }
11235  else
11236  {
11237 $ cpu = IMAGE_FILE_MACHINE_AMD64;
11238 
11239 $ frame.AddrPC .Offset = ctx.Rip;
11240 $ frame.AddrStack.Offset = ctx.Rbp;
11241 $ frame.AddrFrame.Offset = ctx.Rsp;
11242  }
11243 
11244  #else
11245 
11246  {
11247 $ cpu = IMAGE_FILE_MACHINE_I386;
11248 
11249 $ frame.AddrPC .Offset = ctx.Eip;
11250 $ frame.AddrStack.Offset = ctx.Ebp;
11251 $ frame.AddrFrame.Offset = ctx.Esp;
11252  }
11253 
11254  #endif
11255 
11256 $ assert (cpu);
11257 
11258  if (_txSetJmp())
11259  {
11260 $ _txSymGetFromAddr ((void*) 1);
11261  }
11262 $ _txClearJmp();
11263 
11264 $ HANDLE process = GetCurrentProcess();
11265 
11266 $ int frames = 0;
11267 $ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
11268  {
11269 $ DWORD64 prev = frame.AddrStack.Offset;
11270 
11271  // Я злой и страшный серый walk. Я в поросятах знаю talk.
11272 
11273  if (!_txSetJmp()) {$ break; }
11274 
11275 #if defined (_GCC_VER)
11276 
11277  if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
11278  MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
11279 #elif defined (_MSC_VER)
11280 
11281 $ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
11282  Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
11283 #else
11284  #error _GCC_VER / _MSC_VER not defined
11285 #endif
11286  if (frames < 0) {$ continue; }
11287 
11288 $ void* addr = (void*) frame.AddrPC.Offset;
11289 
11290  if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
11291  if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
11292 
11293 $ assert (0 <= frames && frames < (int) szCapture);
11294 
11295 $ capture[frames] = addr;
11296  }
11297 
11298 $ _txClearJmp();
11299 
11300 $ return frames;
11301  }
11302 
11303 // Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
11304 
11305 //-----------------------------------------------------------------------------------------------------------------
11306 
11307 bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
11308  Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
11309  const char** source /*= NULL*/, int context /*= 2*/)
11310  {
11311 $7 static HANDLE process = NULL;
11312 
11313 #if defined (_GCC_VER)
11314  #define LIB_ Win32::MinGW
11315 
11316 #elif defined (_MSC_VER)
11317  #define LIB_ Win32
11318 
11319 #else
11320  #error _GCC_VER / _MSC_VER not defined
11321 #endif
11322 
11323 $ if (!process && addr)
11324  {
11325 $ process = GetCurrentProcess();
11326 
11327 $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
11328  SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
11329 
11330 $ _TX_CALL (LIB_::SymSetOptions, (options));
11331 $ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
11332  }
11333 
11334 $ static DWORD64 mod = 0;
11335 
11336 $ if (module)
11337  {
11338 $ static char sMod [MAX_PATH] = "";
11339 $ memset (sMod, 0, sizeof (sMod));
11340 
11341 $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
11342 
11343 $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
11344 
11345 $ char* ext = strrchr (sMod, '.');
11346  if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
11347 
11348 $ *module = sMod;
11349  }
11350 
11351 $ static char buffer [_TX_BUFSIZE] = "";
11352 $ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
11353 
11354 $ if (symbol)
11355  {
11356 $ memset (buffer, 0, sizeof (buffer));
11357 
11358 $ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
11359 $ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
11360 $ unsigned long long ofs = 0;
11361 
11362 $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
11363 
11364  if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
11365 
11366 $ *symbol = sym;
11367  }
11368 
11369 $ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
11370 
11371 $ if (line)
11372  {
11373 $ memset (&line64, 0, sizeof (line64));
11374 
11375 $ DWORD ofs = 0;
11376 $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
11377 
11378 $ *line = &line64;
11379  }
11380 
11381 $ if (source)
11382  {
11383 $ static char buf [_TX_BUFSIZE] = "";
11384 $ memset (buf, 0, sizeof (buf));
11385 
11386 $ if (line64.FileName && line64.LineNumber)
11387  {
11388 $ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
11389  (int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
11390 
11391 $ *source = buf;
11392  }
11393 
11394  if (!*source || !**source) {$ *source = NULL; }
11395  }
11396 
11397 $ if (!addr && process)
11398  {
11399 $ _TX_CALL (LIB_::SymCleanup, (process));
11400 
11401 $ process = NULL;
11402  }
11403 
11404 $ if (symbol)
11405  {
11406 $ if (strstr (sym->Name, "::TX::") ||
11407  (strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
11408  (strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
11409  strncmp (sym->Name, "_tx_", 4) == 0 ||
11410  strncmp (sym->Name, "tx_", 3) == 0)
11411  {
11412 $ return true;
11413  }
11414 
11415 $ if (!line || !line64.FileName) return false;
11416 
11417 $ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
11418 
11419 $ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
11420  (len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
11421  }
11422 
11423  #undef LIB_
11424 
11425 $ return false;
11426  }
11427 
11428 //-----------------------------------------------------------------------------------------------------------------
11429 
11430 intptr_t _txReadSource (char buf[], intptr_t size, const char file[],
11431  int linStart /*= 0*/, int linEnd /*= INT_MIN*/, int linMark /*= INT_MIN*/)
11432  {
11433 $7 assert (buf);
11434 
11435  if (!file || !*file) {$ return 0; }
11436 
11437  if (linStart < 1) {$ linStart = 1; }
11438  if (linEnd == -1) {$ linEnd = INT_MAX; }
11439 
11440 $ FILE* f = NULL;
11441 $ fopen_s (&f, file, "r");
11442  if (!f) {$ return 0; }
11443 
11444 $ int n = 1;
11445  while (!feof (f))
11446  {
11447  if (n >= linStart) {$ break; }
11448  while (!feof (f) && fgetc (f) != '\n')
11449  ;
11450  n++;
11451  }
11452 
11453 $ char* s = buf;
11454 
11455  #define SZ_ ( size - 3 - (s - buf) )
11456 
11457 $ while (!feof (f) && SZ_ > 0)
11458  {
11459  if (n > linEnd || _txNOP (SZ_) < 0) {$ break; }
11460 
11461  if (linMark != INT_MIN)
11462  {$ s += _tx_snprintf_s (s, SZ_, "%s%5d: ", ((n == linMark)? "=>" : " "), n); }
11463 
11464 $ int c = 0;
11465 $ while (!feof (f) && SZ_ > 0 && (c = fgetc (f)) != '\n') *s++ = (char) c;
11466  if (c == EOF) {$ s--; }
11467 
11468  if (SZ_ > 0) {$ *s++ = '\n'; }
11469 $ n++;
11470  }
11471 
11472  if (n <= linEnd && SZ_ <= 0)
11473  {$ s += _tx_snprintf_s (s, size - (s - buf), "..."); }
11474 
11475  #undef SZ_
11476 
11477 $ fclose (f);
11478 
11479  if (s > buf && s[-1] == '\n') {$ s--; }
11480 $ *s = 0;
11481 
11482 $ return (s - buf);
11483  }
11484 
11485 //-----------------------------------------------------------------------------------------------------------------
11486 
11487 const char* _txCaptureStackBackTraceTX (int framesToSkip /*= 0*/, bool readSource /*= false*/)
11488  {
11489 $6 const int maxFrames = 62; // TX says too: < 63
11490 $ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
11491 
11492  if (framesToSkip == -1) {$ return trace; }
11493 
11494 $ memset (trace, 0, sizeof (trace));
11495 $ char* s = trace;
11496 
11497  #define SZ_ ( sizeof (trace) - 1 - 3 - (s-trace) )
11498 
11499 $ const _txLoc* loc = &_txLoc::Cur;
11500 
11501  for (int i = 0; loc && i < framesToSkip + 1; i++, loc = loc->prev) { $; }
11502 
11503 $ for (int i = -framesToSkip; loc && i < maxFrames; i++, loc = loc->prev)
11504  {
11505  if (i < 0) {$ continue; }
11506 
11507  if (loc->func || loc->file || loc->line)
11508  {
11509 $ s += _tx_snprintf_s (s, SZ_, "%s#%2d in %s at %s (%d)", (i? readSource? "\n\n" : "\n" : ""),
11510  i, loc->func, loc->file, loc->line);
11511 
11512 $ if (readSource)
11513  {
11514 $ s += _tx_snprintf_s (s, SZ_, ":\n\n");
11515 $ s += _txReadSource (s, SZ_, loc->file, loc->line - 2, loc->line + 2, loc->line);
11516  }
11517  }
11518  }
11519 
11520  #undef SZ_
11521 
11522 $ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
11523 
11524 $ return trace;
11525  }
11526 
11527 //-----------------------------------------------------------------------------------------------------------------
11528 
11529 bool _txCreateMiniDump (EXCEPTION_POINTERS* exc)
11530  {
11531 $6 assert (exc);
11532 
11533 $ DWORD winErr = GetLastError();
11534 $ int crtErr = errno;
11535  #if !defined (__CYGWIN__)
11536 $ unsigned long dosErr = _doserrno;
11537  #endif
11538 
11539 $ static char dumpName[MAX_PATH] = "";
11540  if (!*dumpName) {$ _tx_snprintf_s (dumpName, sizeof (dumpName) - 1, "%s.dmp", _txLogName); }
11541 
11542 $ HANDLE file = CreateFile (dumpName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
11543 
11544  if (!file || file == INVALID_HANDLE_VALUE) {$ return false; }
11545 
11546 $ Win32::MINIDUMP_EXCEPTION_INFORMATION excInfo = { GetCurrentThreadId(), exc, false };
11547 
11548 $ bool ok = _TX_CALL (Win32::MiniDumpWriteDump, (GetCurrentProcess(), GetCurrentProcessId(), file,
11549  (Win32::MINIDUMP_TYPE) (Win32::MiniDumpWithIndirectlyReferencedMemory | Win32::MiniDumpScanMemory),
11550  &excInfo, NULL, NULL));
11551 $ CloseHandle (file);
11552 
11553 $ SetLastError (winErr);
11554 $ errno = crtErr;
11555  #if !defined (__CYGWIN__)
11556 $ _doserrno = dosErr;
11557  #endif
11558 
11559  if (ok) {$ return true; }
11560  else {$ return false; }
11561  }
11562 
11563 //-----------------------------------------------------------------------------------------------------------------
11564 //}
11565 
11566 //-----------------------------------------------------------------------------------------------------------------
11567 //{ Errors reporting
11568 //-----------------------------------------------------------------------------------------------------------------
11569 
11570 const char* _txProcessError (const char file[], int line, const char func[], unsigned color, const char msg[], va_list args)
11571  {
11572  _txErrors++;
11573 
11574  DWORD winErr = GetLastError();
11575 
11576  int crtErr = errno;
11577 
11578  #if !defined (__CYGWIN__)
11579  unsigned long dosErr = _doserrno;
11580  #else
11581  unsigned long dosErr = 0;
11582  #endif
11583 
11584  unsigned oglErr = _TX_CALL (Win32::wglGetCurrentDC, ())? _TX_CALL (Win32::glGetError, ()) : _txOGLError;
11585 
11586  unsigned threadId = GetCurrentThreadId();
11587 
11588  enum { isFatal = 0x01, isWarning = 0x02, noMsgBox = 0x04, fmtOnly = 0x08, traceSE = 0x10 };
11589  unsigned options = 0;
11590 
11591  for (; msg && *msg; msg++)
11592  {
11593  if (*msg == '\a') options |= isFatal;
11594  else if (*msg == '\v') options |= isWarning;
11595  else if (*msg == '\b') options |= noMsgBox;
11596  else if (*msg == '\f') options |= fmtOnly;
11597  else if (*msg == '\t') options |= traceSE;
11598  else break;
11599  }
11600 
11601  const char* stkTrace = NULL;
11602  const char* txTrace = NULL; (void) txTrace;
11603 
11604  if (!(options & fmtOnly))
11605  {
11606  stkTrace = ((options & traceSE) && *_txTraceSE)? _txTraceSE : _txCaptureStackBackTrace (2, true);
11607  txTrace = _txCaptureStackBackTraceTX (0, true);
11608  }
11609 
11610  static char what[_TX_BIGBUFSIZE*10] = "";
11611  static char str [_TX_BIGBUFSIZE] = "";
11612  char *s = what;
11613 
11614  #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (what) - 1 - (s - what), ##__VA_ARGS__)
11615  #define VPRINT_(...) s += _tx_vsnprintf_s (s, sizeof (what) - 1 - (s - what), ##__VA_ARGS__)
11616 
11617  PRINT_ ("TXLib %s\n\n", ((options & isWarning)? "предупреждает:" :
11618  (options & isFatal)? "соболезнует..." :
11619  "сообщает:"));
11620  PRINT_ ("Программа: %s", txGetModuleFileName());
11621  if (file) PRINT_ (", файл: %s", file);
11622  if (line) PRINT_ (", строка: %d", line);
11623  if (func) PRINT_ (", функция: %s", func);
11624  PRINT_ (",\n\n");
11625 
11626  if (msg) PRINT_ ("%s: ", (file || line || func)? "Сообщение : "ВНЕЗАПНО"), VPRINT_ (msg, args); while (s > what && s[-1] == '\n') s--; PRINT_ ("\n\n" "#%d: %s, Instance: 0x%p (%d-bit), Flags: %c%c%c%c%c%d, Thread: 0x%X%s", _txErrors, _TX_VERSION, &_txInitialized, (sizeof (void*) == 4)? 32 : 64, "cC"[_txConsole], "mM"[_txMain], "dD"[_txIsDll], "rR"[_txRunning], "eE"[_txExit], _txLoc::Cur.trace, threadId, (threadId == _txMainThreadId)? " (Main)" : (threadId == _txCanvas_ThreadId)? " (Canvas)" : ""); if (winErr) PRINT_ (", GetLastError(): %lu (", (unsigned long) winErr), s += FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, winErr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), s, (DWORD) (sizeof (what) - (s-what)), NULL) - 2, s -= (s[-1] == '.')? 1 : 0, PRINT_ (")"); if (crtErr) PRINT_ (", errno: %d (%s)", crtErr, (strerror_s (str, sizeof (str), crtErr), str)); if (dosErr) PRINT_ (", _doserrno: %lu (%s)", dosErr, (strerror_s (str, sizeof (str), dosErr), str)); if (oglErr) PRINT_ (", glGetError(): %u (0x%04X, %s)", oglErr, oglErr, _TX_CALL (Win32::gluErrorString, (oglErr))); #if (__cplusplus <= 201703L) PRINT_ (". %s\n", ::std::uncaught_exception ()? "std::uncaught_exception(): true." : ""); #else PRINT_ (". %s\n", ::std::uncaught_exceptions()? "std::uncaught_exceptions(): true." : ""); #endif if (_txLoc::Cur.inTX > 0 && file && !(_txLoc::Cur.line == line && _stricmp (_txLoc::Cur.file, file) == 0) && (_txLoc::Cur.file || _txLoc::Cur.line || _txLoc::Cur.func)) PRINT_ ("From: %s (%d) %s.\n", _txLoc::Cur.file, _txLoc::Cur.line, _txLoc::Cur.func); txOutputDebugPrintf ("\r" "%s - ERROR: %s\n", _TX_VERSION, what); if (options & fmtOnly) { SetLastError (winErr); errno = crtErr; #if !defined (__CYGWIN__) _doserrno = dosErr; #endif return what; } unsigned restore = txGetConsoleAttr(); txSetConsoleAttr ((options & isFatal)? FOREGROUND_LIGHTMAGENTA : FOREGROUND_LIGHTRED); if (color) {$ txSetConsoleAttr (color); } int oldCodePage = txSetLocale(); fprintf (stderr, "\n" "--------------------------------------------------\n" "%s\n" "--------------------------------------------------\n", what); if (stkTrace && strstr (stkTrace, ".exe: ")) {$ fprintf (stderr, "Стек вызовов:\n\n" "%s\n\n" "--------------------------------------------------\n", stkTrace); } SetConsoleOutputCP (oldCodePage); txSetConsoleAttr (restore); if (*_txLogName) do { FILE* log = NULL; fopen_s (&log, _txLogName, "a"); if (!log) {$ break; } fprintf (log, "\n" "--------------------------------------------------\n" "%s\n" "--------------------------------------------------\n", what); fprintf (log, "Стек вызовов:\n\n" "%s\n", (*_txTraceSE? _txTraceSE : stkTrace)); #if defined (_TX_ALLOW_TRACE) || defined (_DEBUG) if (_txLoc::Cur.inTX > 0 && txTrace && *txTrace) { fprintf (log, "\n" "--------------------------------------------------\n" "Стек вызовов TX:\n\n" "%s\n", txTrace); } #endif fprintf (log, "\n" "--------------------------------------------------\n" "%s\n\n" "--------------------------------------------------\n", _txAppInfo()); fclose (log); break; } while (false); int ret = 0; if (!(options & noMsgBox)) { txSleep (_txWindowUpdateInterval); PRINT_ ("\n" "Прервать программу?"); ret = txMessageBox (what, ((options & isWarning)? "Предупреждение" : (options & isFatal)? "Фатальная ошибка" : "Ошибка в программе"), MB_ICONSTOP | MB_SYSTEMMODAL | MB_YESNOCANCEL); } SetLastError (winErr); errno = crtErr; #if !defined (__CYGWIN__) _doserrno = dosErr; #endif if (((options & isFatal) && !IsDebuggerPresent()) || ret == IDYES) { txUnlock(); _txCleanup(); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } #undef PRINT_ return what; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) int _txOnErrorReport (int type, const char* text, int* ret) { assert (text); assert (ret); _txErrors++; unsigned restore = txGetConsoleAttr(); switch (type) { case _CRT_WARN: txSetConsoleAttr (FOREGROUND_LIGHTRED); break; case _CRT_ERROR: txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA); break; case _CRT_ASSERT: txSetConsoleAttr (FOREGROUND_YELLOW); break; default: break; } const char startReport[] = "Detected memory leaks!\n", endReport[] = "Object dump complete.\n"; if (strcmp (text, startReport) == 0) // Dirty, dirty hack. А что делать? { _txOnErrorReport (type, "\n", NULL); _txOnErrorReport (type, _TX_VERSION " - ERROR: ", NULL); _txOnErrorReport (type, "Внимание: Обнаружены утечки памяти! (Для поиска используйте _TX_ALLOC_BREAK.)\n", NULL); _txOnErrorReport (type, "\n", NULL); } size_t len = strlen (text); if (text [len-1] != '\n') txOutputDebugPrintf ("%s", text); else if (strcmp (text, endReport) != 0) txOutputDebugPrintf ("%s" "%s - ERROR: ", text, _TX_VERSION); else txOutputDebugPrintf ("%s\n", text); DWORD n = 0; HANDLE err = GetStdHandle (STD_ERROR_HANDLE); WriteFile (err, text, (DWORD) strlen (text), &n, NULL); txSetConsoleAttr (restore); if (*_txLogName) do { HANDLE log = CreateFile (_txLogName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (log == INVALID_HANDLE_VALUE) break; SetFilePointer (log, 0, NULL, FILE_END); WriteFile (log, text, (DWORD) strlen (text), &n, NULL); CloseHandle (log); break; } while (false); if (ret) *ret = 0; return (type == _CRT_WARN); } #endif //----------------------------------------------------------------------------------------------------------------- int txMessageBox (const char text[], const char header[], unsigned flags /*= MB_ICONINFORMATION | MB_OKCANCEL*/) { $5 static wchar_t textW [_TX_BIGBUFSIZE * sizeof (wchar_t)] = L"[NULL text]"; $ static wchar_t headerW [_TX_BUFSIZE * sizeof (wchar_t)] = L"[NULL header]"; if (text) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, text, -1, textW, sizearr (textW)) || memset (textW, 0, sizeof (textW)); } if (header) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, header, -1, headerW, sizearr (headerW)) || memset (headerW, 0, sizeof (headerW)); } $ HWND wnd = _txCanvas_Window; $ int ret = MessageBoxW ((wnd && IsWindowVisible (wnd))? wnd : _TX_CALL (Win32::GetConsoleWindow,()), textW, headerW, flags | MB_SETFOREGROUND | MB_TOPMOST | MB_OKCANCEL ); $ if (ret == IDCANCEL) { $ SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0); $ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT); } $ return ret; } //----------------------------------------------------------------------------------------------------------------- bool txGetAsyncKeyState (int key) { $1 HWND wnd = GetForegroundWindow(); return (GetAsyncKeyState (key) & 0x8000) && (wnd == txWindow() || wnd == Win32::GetConsoleWindow()); } //----------------------------------------------------------------------------------------------------------------- bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...) { $5 if (_TX_ARGUMENT_FAILED (format)) return false; $ va_list arg; va_start (arg, format); $ bool ok = true; #if defined (_WIN32_IE) && (_WIN32_IE >= 0x0500) $ NOTIFYICONDATA nid = { sizeof (nid) }; $ nid.uFlags = NIF_ICON | NIF_TIP | NIF_INFO; $ nid.hWnd = NULL; $ nid.uID = 1; $ nid.hIcon = _txCreateTXIcon (16); assert (nid.hIcon); $ strncpy_s (nid.szTip, sizeof (nid.szTip), "TXLib Information", sizeof (nid.szTip)); $ strncpy_s (nid.szInfoTitle, sizeof (nid.szInfoTitle), (title? title : "TXLib сообщает"), sizeof (nid.szInfoTitle) - 1); $ _tx_vsnprintf_s (nid.szInfo, sizeof (nid.szInfo), format, arg); $ nid.dwInfoFlags = flags; $ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification)\n", nid.szInfoTitle, nid.szInfo); $ ok &= !!Shell_NotifyIcon (NIM_ADD, (::NOTIFYICONDATA*) &nid); $ ok &= !!Shell_NotifyIcon (NIM_MODIFY, (::NOTIFYICONDATA*) &nid); $ if (nid.hIcon) DestroyIcon (nid.hIcon) asserted; #else $ char nid_szInfo[_TX_BUFSIZE] = ""; $ _tx_vsnprintf_s (nid_szInfo, sizeof (nid_szInfo), format, arg); $ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification - NOT displayed)\n", title, nid_szInfo); $ ok = false; $ (void)flags; (void)title; #endif $ va_end (arg); return ok; } //----------------------------------------------------------------------------------------------------------------- void _txTrace (const char file[], int line, const char func[], const char msg[] /*= NULL*/, ...) { unsigned id = GetCurrentThreadId(); const char marks[2][2][3] = {{"uU", "cC"}, {"mM", "??"}}; char mark = marks [id == _txMainThreadId] [id == _txCanvas_ThreadId] [(_txLoc::Cur.inTX > 0)]; char msgStr[_TX_BUFSIZE] = ""; if (msg) { va_list arg; va_start (arg, msg); _tx_vsnprintf_s (msgStr, sizeof (msgStr) - 1, msg, arg); va_end (arg); } txOutputDebugPrintf ("%s - 0x%p %c%c%c%c%c%d [%c] - %-*s (%5d) " "|%*s%s" "%s%s\n", _TX_VERSION, &_txInitialized, "cC"[_txConsole], "mM"[_txMain], "dD"[_txIsDll], "rR"[_txRunning], "eE"[_txExit], _txLoc::Cur.trace, mark, (int) sizeof (__FILE__) - 1, (file? file : "(NULL file)"), line, 2 * (_txLoc::Cur.inTX - 1) * !!func, "", (func? func : ""), ((*msgStr && func)? ": " : ""), msgStr); } //----------------------------------------------------------------------------------------------------------------- int txOutputDebugPrintf (const char format[], ...) { if (!format) return 0; enum { msgbox = 1, print = 2, compr = 4 }; int options = 0; for (; format && *format; format++) { if (*format == '\a') options |= msgbox; else if (*format == '\f') options |= print; else if (*format == '\r') options |= compr; else break; } char text[_TX_BIGBUFSIZE] = ""; va_list arg; va_start (arg, format); int n = (int) _tx_vsnprintf_s (text, sizeof (text) - 1-1, format, arg); va_end (arg); struct __ { static int trimSpaces (char str[]) { char *dst = str, *src = str; for (char d = ' '; d; src++) if (isspace ((unsigned char)(*src))) { if (d != ' ') *dst++ = d = ' '; } else *dst++ = d = *src; return (int) (dst - str - 1); }}; if (options & compr) n = __::trimSpaces (text); OutputDebugString (text); if (options & print) fprintf (stderr, "%s", text); if (options & msgbox) txMessageBox (text, "Оказывается, что", MB_ICONEXCLAMATION); return n; } //----------------------------------------------------------------------------------------------------------------- intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) { if (!format) return 0; va_list arg; va_start (arg, format); intptr_t ret = _tx_vsnprintf_s (stream, size, format, arg); va_end (arg); return ret; } //----------------------------------------------------------------------------------------------------------------- intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg) { if (!stream || !format) return 0; #if defined (_TRUNCATE) intptr_t ret = _vsnprintf_s (stream, size, _TRUNCATE, format, arg); #else intptr_t ret = _vsnprintf (stream, size, format, arg); #endif if (ret < 0 && size >= 4) { const char ellipsis[] = "..."; size_t szEllipsis = sizeof (ellipsis) - 1; strncpy_s (stream + size - szEllipsis, szEllipsis+1, ellipsis, szEllipsis); } return (ret >= 0)? ret : size; } //----------------------------------------------------------------------------------------------------------------- #if defined (__CYGWIN__) int _getch() { termios oldattr = {}; tcgetattr (STDIN_FILENO, &oldattr); termios newattr = oldattr; newattr.c_lflag &= ~(ICANON | ECHO); tcsetattr (STDIN_FILENO, TCSANOW, &newattr); int ch = getchar(); tcsetattr (STDIN_FILENO, TCSANOW, &oldattr); return ch; } //----------------------------------------------------------------------------------------------------------------- int _putch (int ch) { termios old = {}; tcgetattr (STDOUT_FILENO, &old); termios cur = old; cur.c_lflag &= ~ICANON; cur.c_lflag |= ECHO; tcsetattr (STDOUT_FILENO, TCSANOW, &cur); putchar (ch); tcsetattr (STDOUT_FILENO, TCSANOW, &old); return ch; } //----------------------------------------------------------------------------------------------------------------- int _kbhit() { termios old = {}; tcgetattr (STDIN_FILENO, &old); termios cur = old; cur.c_lflag &= ~(ICANON | ECHO); cur.c_cc[VMIN] = 1; cur.c_cc[VTIME] = 0; tcsetattr (STDIN_FILENO, TCSAFLUSH, &cur); fd_set fd = {}; FD_SET (STDIN_FILENO, &fd); timeval tv = {}; int res = select (STDIN_FILENO + 1, &fd, NULL, NULL, &tv) && FD_ISSET (STDIN_FILENO, &fd); tcsetattr (STDIN_FILENO, TCSAFLUSH, &old); return res; } #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Information //----------------------------------------------------------------------------------------------------------------- const char* txGetModuleFileName (bool fileNameOnly /*= true*/) { static char name[MAX_PATH] = ""; if (!*name) { if (!GetModuleFileName (NULL, name, sizeof (name) - 1)) *name = 0; char* ext = strrchr (name, '.'); if (ext) _strlwr_s (ext, sizeof (name) - 1 - (ext - name)); } assert (*name); if (fileNameOnly) return name; static char fullName[MAX_PATH] = ""; strncpy_s (fullName, sizeof (fullName), name, sizeof (fullName) - 1); char* title = strrchr (fullName, '\\'); if (!title) title = fullName; char* ext = strrchr (fullName, '.'); if (!ext) ext = fullName + strlen (fullName); size_t sz = sizeof (fullName) - (ext - fullName); strncpy_s (ext, sz-1, " - TXLib", sz); return title + 1; } //----------------------------------------------------------------------------------------------------------------- const char* _txAppInfo() { $1 time_t timeT = time (NULL) - clock()/CLOCKS_PER_SEC; char timeS[32] = ""; ctime_s (timeS, sizeof (timeS), &timeT); static char text[_TX_BUFSIZE] = ""; char cwd [MAX_PATH] = ""; _tx_snprintf_s (text, sizeof (text) - 1, "Developed with:\n\n" "The Dumb Artist Library (TX Library)\n" _TX_VERSION "\n" _TX_AUTHOR "\n" "See license on: http://txlib.ru\n\n" "TXLib file:" "\t" __FILE__ "\n" "Compiled:" "\t" __DATE__ " " __TIME__ ", " __TX_COMPILER__ ", %s, %d-bit, " _TX_BUILDMODE "\n" "Started:" "\t" "%.6s %.4s %.8s\n\n" "Run file:" "\t" "%s\n" "Directory:" "\t" "%s", #if defined (_MSC_VER) "MSVC Runtime", #elif defined (__CYGWIN__) "Cygwin Runtime", #elif defined (_GCC_VER) && defined (_WIN64) __mingw_get_crt_info(), #else "MinGW Runtime " TX_QUOTE (__MINGW32_MAJOR_VERSION) "." TX_QUOTE (__MINGW32_MINOR_VERSION), #endif (sizeof (void*) == sizeof (DWORD))? 32 : 64, timeS + 4, timeS + 20, timeS + 11, // These offsets are ANSI standardized txGetModuleFileName(), _getcwd (cwd, sizeof (cwd) - 1)); return text; } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib API implementation // Реализация TXLib API //================================================================================================================= inline const char* txVersion() { return _TX_VERSION; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txVersionNumber() { return _TX_VER; } //----------------------------------------------------------------------------------------------------------------- inline HWND txWindow() { $0 return _txCanvas_Window; } //----------------------------------------------------------------------------------------------------------------- inline HDC& txDC() { $0 return _txCanvas_BackBuf[0]; } //----------------------------------------------------------------------------------------------------------------- inline RGBQUAD* txVideoMemory() { return _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- POINT txGetExtent (HDC dc /*= txDC()*/) { $0 static POINT err = {-1, -1}; if (!dc) {$ POINT screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; return screen; }; if (_TX_DEFAULT_HDC_FAILED (dc)) {$ return err; } $ BITMAP bmap = {}; $ txGDI (Win32::GetObject (Win32::GetCurrentObject (dc, OBJ_BITMAP), sizeof (bmap), &bmap), dc) asserted; $ POINT size = { bmap.bmWidth, bmap.bmHeight }; $ return size; } //----------------------------------------------------------------------------------------------------------------- int txGetExtentX (HDC dc /*= txDC()*/) { return txGetExtent (dc) .x; } //----------------------------------------------------------------------------------------------------------------- int txGetExtentY (HDC dc /*= txDC()*/) { return txGetExtent (dc) .y; } //----------------------------------------------------------------------------------------------------------------- bool txDestroyWindow (HWND wnd /*= txWindow()*/) { $1 if (!wnd || !txWindow()) return false; $ if (wnd != txWindow()) { $ return !!SendNotifyMessage (txWindow(), _TX_WM_DESTROYWND, 0, (LPARAM) wnd); } $ if (SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0) == 0) return false; $ if (_txMain) { $ txNotifyIcon (NIIF_WARNING, NULL, "\n" "Очень, очень плохо завершать программу через txDestroyWindow().\n\n" "Возвращайтесь через main(), там вам будут рады.\n"); $ Sleep (_TX_TIMEOUT); } $ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT); $ return _txCanvas_Window == NULL; } //----------------------------------------------------------------------------------------------------------------- HPEN txSetColor (COLORREF color, double thickness /*= 1*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HPEN pen = Win32::CreatePen ((color == TX_TRANSPARENT? PS_NULL : PS_SOLID), ROUND (thickness), color); $ if (!pen) return (HPEN) NULL; $ if (!_txBuffer_Select (pen, dc)) { $ Win32::DeleteObject (pen); $ return (HPEN) NULL; } $ if (txGDI (Win32::SetTextColor (dc, color), dc) == CLR_INVALID) {$ return (HPEN) NULL; } $ return pen; } //----------------------------------------------------------------------------------------------------------------- COLORREF txColor (double red, double green, double blue) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)); $ return txSetColor (color)? color : CLR_INVALID; } //----------------------------------------------------------------------------------------------------------------- COLORREF txGetColor (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_PEN)), dc); $ assert (obj); if (!obj) return CLR_INVALID; $ union { EXTLOGPEN extLogPen; LOGPEN LogPen; } buf = {}; $ int size = Win32::GetObject (obj, 0, NULL); $ Win32::GetObject (obj, sizeof (buf), &buf) asserted; $ return (size == sizeof (LOGPEN))? buf.LogPen.lopnColor : buf.extLogPen.elpColor; } //----------------------------------------------------------------------------------------------------------------- HBRUSH txSetFillColor (COLORREF color, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HBRUSH brush = (color == TX_TRANSPARENT)? (HBRUSH) Win32::GetStockObject (HOLLOW_BRUSH) : Win32::CreateSolidBrush (color); $ return (_txBuffer_Select (brush, dc))? brush : (Win32::DeleteObject (brush), (HBRUSH) NULL); } //----------------------------------------------------------------------------------------------------------------- COLORREF txFillColor (double red, double green, double blue) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)); $ return txSetFillColor (color)? color : CLR_INVALID; } //----------------------------------------------------------------------------------------------------------------- COLORREF txGetFillColor (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_BRUSH)), dc); $ assert (obj); if (!obj) return CLR_INVALID; $ LOGBRUSH buf = {}; $ txGDI ((Win32::GetObject (obj, sizeof (buf), &buf)), dc) asserted; $ return buf.lbColor; } //----------------------------------------------------------------------------------------------------------------- bool txClear (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ return txGDI (!!(Win32::PatBlt (dc, 0, 0, size.x, size.y, PATCOPY)), dc); } //----------------------------------------------------------------------------------------------------------------- inline bool txSetPixel (double x, double y, COLORREF color, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ txGDI ((Win32::SetPixel (dc, ROUND (x), ROUND (y), color)), dc); $ return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txPixel (double x, double y, double red, double green, double blue, HDC dc /*= txDC()*/) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ return txSetPixel (x, y, RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)), dc); } //----------------------------------------------------------------------------------------------------------------- inline COLORREF txGetPixel (double x, double y, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ return txGDI ((Win32::GetPixel (dc, ROUND (x), ROUND (y))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txLine (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ bool ok = txGDI ((Win32::MoveToEx (dc, ROUND (x0), ROUND (y0), NULL)), dc); $ ok &= txGDI ((Win32::LineTo (dc, ROUND (x1), ROUND (y1) )), dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txRectangle (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::Rectangle (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txPolygon (const POINT points[], int numPoints, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (points)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI (!!(Win32::Polygon (dc, points, numPoints)), dc); } //----------------------------------------------------------------------------------------------------------------- bool txEllipse (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::Ellipse (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txCircle (double x, double y, double r) { $1 return txEllipse (x-r, y-r, x+r, y+r); } //----------------------------------------------------------------------------------------------------------------- bool txArc (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Arc (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txPie (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Pie (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txChord (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Chord (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txFloodFill (double x, double y, COLORREF color /*= TX_TRANSPARENT*/, DWORD mode /*= FLOODFILLSURFACE*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ if (color == TX_TRANSPARENT) color = txGetPixel (x, y, dc); $ return txGDI (!!(Win32::ExtFloodFill (dc, ROUND (x), ROUND (y), color, mode)), dc); } //----------------------------------------------------------------------------------------------------------------- bool txTextOut (double x, double y, const char text[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (text)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ int len = (int) strlen (text); $ bool ok = txGDI (!!(Win32::TextOut (dc, ROUND (x), ROUND (y), text, len)), dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txDrawText (double x0, double y0, double x1, double y1, const char text[], unsigned format /*= DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS*/, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (text)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; #if !defined (NDEBUG) $ if (x0 > x1) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Параметр x0 = %lg больше, чем x1 = %lg. Текст выводиться не будет.", x0, x1); } $ if (y0 > y1) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Параметр y0 = %lg больше, чем y1 = %lg. Текст выводиться не будет.", y0, y1); } #endif $ RECT r = { ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1) }; $ if (!strchr (text, '\n')) format |= DT_SINGLELINE; $ unsigned prev = txSetTextAlign (TA_LEFT | TA_TOP | TA_NOUPDATECP, dc); $ bool ok = false; $ if (Win32::DrawText) { $ ok = !!txGDI ((Win32::DrawText (dc, text, -1, &r, format)), dc); $ Win32::GetPixel (dc, 0, 0); $ ok = true; } else { $ txTextOut ((x0 + x1) / 2, (y0 + y1) / 2, text); $ ok = false; } $ txSetTextAlign (prev, dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- HFONT txSelectFont (const char name[], double sizeY, double sizeX /*= -1*/, int bold /*= FW_DONTCARE*/, bool italic /*= false*/, bool underline /*= false*/, bool strikeout /*= false*/, double angle /*= 0*/, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (name)) return NULL; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HFONT font = txFontExist (name)? Win32::CreateFont (ROUND (sizeY), ROUND ((sizeX >= 0)? sizeX : sizeY/3), ROUND (angle*10), 0, bold, italic, underline, strikeout, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, name) : (HFONT) Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, dc); $ return font; } //----------------------------------------------------------------------------------------------------------------- SIZE txGetTextExtent (const char text[], HDC dc /*= txDC()*/) { $1 SIZE size = {-1, -1}; $ if (_TX_ARGUMENT_FAILED (text)) return size; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return size; $ size_t len = strlen (text); $ txGDI ((Win32::GetTextExtentPoint32 (dc, text, (int) len, &size)), dc) asserted; $ return size; } //----------------------------------------------------------------------------------------------------------------- int txGetTextExtentX (const char text[], HDC dc /*= txDC()*/) { $1 return txGetTextExtent (text, dc) .cx; } //----------------------------------------------------------------------------------------------------------------- int txGetTextExtentY (const char text[], HDC dc /*= txDC()*/) { $1 return txGetTextExtent (text, dc) .cy; } //----------------------------------------------------------------------------------------------------------------- unsigned txSetTextAlign (unsigned align /*= TA_CENTER | TA_BASELINE*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::SetTextAlign (dc, align)), dc); } //----------------------------------------------------------------------------------------------------------------- LOGFONT* txFontExist (const char name[]) { $1 if (_TX_ARGUMENT_FAILED (name)) return NULL; $ static LOGFONT font = {}; $ font.lfCharSet = DEFAULT_CHARSET; $ strncpy_s (font.lfFaceName, sizeof (font.lfFaceName), name, sizeof (font.lfFaceName) - 1); $ struct tools { static int CALLBACK enumFonts (const LOGFONT* fnt, const TEXTMETRIC*, DWORD, LPARAM data) { $ if (_TX_ARGUMENT_FAILED (fnt)) return 0; $ if (_TX_ARGUMENT_FAILED (data)) return 0; #ifndef __STRICT_ANSI__ $ return _strnicmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE); #else $ return strncmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE); #endif } }; $ return txGDI ((Win32::EnumFontFamiliesEx (NULL, &font, tools::enumFonts, (LPARAM) &font, 0)), NULL) == 0? &font : NULL; } //----------------------------------------------------------------------------------------------------------------- bool txSelectObject (HGDIOBJ obj, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (obj)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return _txBuffer_Select (obj, dc); } //----------------------------------------------------------------------------------------------------------------- HDC txCreateCompatibleDC (double sizeX, double sizeY, HBITMAP bitmap /*= NULL*/) { $1 POINT size = { ROUND (sizeX), ROUND (sizeY) }; $ HDC dc = _txBuffer_Create (NULL, &size, bitmap); $ assert (dc); if (!dc) return NULL; $ txSetDefaults (dc); $ if (!_txCanvas_UserDCs) return dc; $ txAutoLock _lock; $ _txCanvas_UserDCs->push_back (dc); $ if (_txCanvas_UserDCs->size() >= _TX_BUFSIZE) {$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загрузили уже %d HDC, системе может стать плохо.", (int) _txCanvas_UserDCs->size()); } $ return dc; } //----------------------------------------------------------------------------------------------------------------- HDC txCreateDIBSection (double sizeX, double sizeY, RGBQUAD** pixels /*= NULL*/) { $1 RGBQUAD* buf = NULL; $ if (!pixels) pixels = &buf; $ BITMAPINFO info = {{ sizeof (info), ROUND (sizeX), ROUND (sizeY), 1, WORD (sizeof (RGBQUAD) * 8), BI_RGB }}; $ HDC dc = txCreateCompatibleDC (0, 0, Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (void**) pixels, NULL, 0)); $ RGBQUAD black = { 0, 0, 0, 255 }; $ for (int i = 0; i < sizeX * sizeY; i++) (*pixels)[i] = black; $ return dc; } //----------------------------------------------------------------------------------------------------------------- HDC txCreateDIBSection (double sizeX, double sizeY, COLORREF** pixels) { $1 return txCreateDIBSection (sizeX, sizeY, (RGBQUAD**) pixels); } //----------------------------------------------------------------------------------------------------------------- HDC txLoadImage (const char filename[], unsigned imageFlags /*= IMAGE_BITMAP*/, unsigned loadFlags /*= LR_LOADFROMFILE*/) { $1 if (_TX_ARGUMENT_FAILED (filename && *filename)) return NULL; $ HBITMAP image = (HBITMAP) Win32::LoadImage ((loadFlags & LR_LOADFROMFILE)? NULL : GetModuleHandle (NULL), filename, imageFlags, 0, 0, loadFlags); $ if (!image) return NULL; $ HDC dc = txCreateCompatibleDC (0, 0, image); $ if (!(loadFlags & LR_LOADFROMFILE)) return dc; $ static std::map <std::string, unsigned> loadTimes; $ std::string file = filename; $ unsigned time = GetTickCount(); $ if ((long) (time - loadTimes [file]) < _TX_TIMEOUT) {$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загружаете \"%s\" слишком часто, программа будет тормозить.", filename); } $ loadTimes [file] = time; $ return dc; } //----------------------------------------------------------------------------------------------------------------- bool txDeleteDC (HDC* pdc) { $1 if (_TX_ARGUMENT_FAILED (pdc)) return false; $ HDC dc = *pdc; $ bool ok = _txBuffer_Delete (pdc); $ if (!ok) return false; $ if (!_txCanvas_UserDCs) return ok; $ txAutoLock _lock; $ std::vector <HDC> ::iterator it = std::find (_txCanvas_UserDCs->begin(), _txCanvas_UserDCs->end(), dc); $ if (it != _txCanvas_UserDCs->end()) { std::swap (*it, _txCanvas_UserDCs->back()); _txCanvas_UserDCs->pop_back(); } $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txDeleteDC (HDC dc) { $1 return txDeleteDC (&dc); } //----------------------------------------------------------------------------------------------------------------- bool txBitBlt (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, unsigned operation /*= SRCCOPY*/) { $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; $ return txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), operation)), destImage); } //----------------------------------------------------------------------------------------------------------------- inline bool txBitBlt (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txBitBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource); } //----------------------------------------------------------------------------------------------------------------- bool txTransparentBlt (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, COLORREF transColor /*= TX_BLACK*/) { $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; #if !defined (NDEBUG) $ if (!(0 <= xSource && xSource + width <= size.x && 0 <= ySource && ySource + height <= size.y)) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, " "функция txTransparentBlt() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y); } #endif $ bool ok = (Win32::TransparentBlt != NULL); $ if (ok) { $ ok &= txGDI (!!(Win32::TransparentBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), transColor)), destImage); } else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); } return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txTransparentBlt (double xDest, double yDest, HDC sourceImage, COLORREF transColor /*= TX_BLACK*/, double xSource /*= 0*/, double ySource /*= 0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txTransparentBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, transColor); } //----------------------------------------------------------------------------------------------------------------- bool txAlphaBlend (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { // Это проверки того, правильные ли HDC вы передали в функцию. // Не бойтесь долларов - <s>это не запрещенная валюта</s> это макросы для отладки TXLib'а. $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; // Это автоматическое определение размеров картинки (точнее, HDC источника - source) с помощью txGetExtent(). $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; // Это проверка того, что картинка (или ее часть) правильно попадает в окно (точнее, HDC приемника - destination, dest). // Если она "вылезает" из окна в любую сторону, то Win32::AlphaBlend не будет работать. Эта проверка происходит только // в режиме отладки (когда не задан макрос NDEBUG - No Debugging, без отладки). #if !defined (NDEBUG) $ if (!(0 <= xSource && xSource + width <= size.x && 0 <= ySource && ySource + height <= size.y)) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, " "функция txAlphaBlend() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y); } #endif // Это на случай, если параметр alpha вылезает за диапазон [0..1]. $ if (alpha < 0) alpha = 0; $ if (alpha > 1) alpha = 1; // Об этом см. ниже. $ BITMAP bmap = { 0, 0, 0, 0, 0, 24 }; $ bool ok = !!Win32::GetObject (txGDI ((Win32::GetCurrentObject (sourceImage, OBJ_BITMAP)), sourceImage), sizeof (bmap), &bmap); // // * Структура BLENDFUNCTION * // // Эта структура определяет, как цвета пикселей окна (точнее, source DC) и картинки смешиваются при рисовании с учетом // прозрачности. // // Параметры смешивания помещаются в группу из переменных - так называемую структуру. Переменные, входящие в структуру, // называются компонентами структуры. // // В этой структуре более всех важен ее третий компонент (см. ниже), задающий прозрачность картинки. // Если он равен нулю - то картинка будет считаться полностью прозрачной и вызов Win32::AlphaBlend ничего не нарисует. // Если он равен 255 - то картинка скопируется в окно полностью, без эффекта прозрачности. // // С помощью четвертого компонента структуры система рисования Windows (Win32 GDI) узнает, надо ли учитывать альфа-канал // в копируемой картинке или его надо игнорировать. Если в картинке фактически есть правильно построенный альфа-канал // (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу // AC_SRC_ALPHA. // // Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0. // // В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения // функции txAlphaBlend(). // // Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать // альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы // породите невнятный паленый код и безнадежно испортите себе карму. :(( // // На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо. // // ____1____ 2 ___________3___________ _______________________4________________________ // / \ | / \ / \. $ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) }; // // * Вызов стандартной функции Win32::AlphaBlend() * // // Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend() // вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите // "Windows AlphaBlend function" и почитайте про ее параметры. // // Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не // совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s> // это сделано для упрощения вызова функции txAlphaBlend(). $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<-- { // // vvvvv vvvvvv // <<-- $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<== sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<== destImage); // ^^^^^ ^^^^^^ // <<-- } // ||||| |||||| // <<-- // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<-- else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); $ ok = false; } // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки : "ВНЕЗАПНО"),
11627  VPRINT_ (msg, args);
11628 
11629  while (s > what && s[-1] == '\n') s--;
11630 
11631  PRINT_ ("\n\n" "#%d: %s, Instance: 0x%p (%d-bit), Flags: %c%c%c%c%c%d, Thread: 0x%X%s",
11632 
11633  _txErrors, _TX_VERSION, &_txInitialized, (sizeof (void*) == 4)? 32 : 64,
11634 
11635  "cC"[_txConsole], "mM"[_txMain], "dD"[_txIsDll], "rR"[_txRunning], "eE"[_txExit], _txLoc::Cur.trace,
11636 
11637  threadId, (threadId == _txMainThreadId)? " (Main)" :
11638  (threadId == _txCanvas_ThreadId)? " (Canvas)" : "");
11639 
11640  if (winErr) PRINT_ (", GetLastError(): %lu (", (unsigned long) winErr),
11641  s += FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
11642  NULL, winErr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
11643  s, (DWORD) (sizeof (what) - (s-what)), NULL) - 2,
11644  s -= (s[-1] == '.')? 1 : 0,
11645  PRINT_ (")");
11646 
11647  if (crtErr) PRINT_ (", errno: %d (%s)", crtErr, (strerror_s (str, sizeof (str), crtErr), str));
11648  if (dosErr) PRINT_ (", _doserrno: %lu (%s)", dosErr, (strerror_s (str, sizeof (str), dosErr), str));
11649  if (oglErr) PRINT_ (", glGetError(): %u (0x%04X, %s)", oglErr, oglErr, _TX_CALL (Win32::gluErrorString, (oglErr)));
11650 
11651  #if (__cplusplus <= 201703L)
11652  PRINT_ (". %s\n", ::std::uncaught_exception ()? "std::uncaught_exception(): true." : "");
11653  #else
11654  PRINT_ (". %s\n", ::std::uncaught_exceptions()? "std::uncaught_exceptions(): true." : "");
11655  #endif
11656 
11657  if (_txLoc::Cur.inTX > 0 && file && !(_txLoc::Cur.line == line && _stricmp (_txLoc::Cur.file, file) == 0) &&
11658  (_txLoc::Cur.file || _txLoc::Cur.line || _txLoc::Cur.func))
11659  PRINT_ ("From: %s (%d) %s.\n", _txLoc::Cur.file, _txLoc::Cur.line, _txLoc::Cur.func);
11660 
11661  txOutputDebugPrintf ("\r" "%s - ERROR: %s\n", _TX_VERSION, what);
11662 
11663  if (options & fmtOnly)
11664  {
11665  SetLastError (winErr);
11666 
11667  errno = crtErr;
11668 
11669  #if !defined (__CYGWIN__)
11670  _doserrno = dosErr;
11671  #endif
11672 
11673  return what;
11674  }
11675 
11676  unsigned restore = txGetConsoleAttr();
11677  txSetConsoleAttr ((options & isFatal)? FOREGROUND_LIGHTMAGENTA : FOREGROUND_LIGHTRED);
11678  if (color) {$ txSetConsoleAttr (color); }
11679 
11680  int oldCodePage = txSetLocale();
11681 
11682  fprintf (stderr, "\n" "--------------------------------------------------\n"
11683  "%s\n"
11684  "--------------------------------------------------\n",
11685  what);
11686 
11687  if (stkTrace && strstr (stkTrace, ".exe: "))
11688  {$ fprintf (stderr, "Стек вызовов:\n\n"
11689  "%s\n\n"
11690  "--------------------------------------------------\n",
11691  stkTrace); }
11692 
11693  SetConsoleOutputCP (oldCodePage);
11694  txSetConsoleAttr (restore);
11695 
11696  if (*_txLogName) do
11697  {
11698  FILE* log = NULL; fopen_s (&log, _txLogName, "a");
11699  if (!log) {$ break; }
11700 
11701  fprintf (log, "\n" "--------------------------------------------------\n"
11702  "%s\n"
11703  "--------------------------------------------------\n",
11704  what);
11705 
11706  fprintf (log, "Стек вызовов:\n\n"
11707  "%s\n",
11708  (*_txTraceSE? _txTraceSE : stkTrace));
11709 
11710  #if defined (_TX_ALLOW_TRACE) || defined (_DEBUG)
11711 
11712  if (_txLoc::Cur.inTX > 0 && txTrace && *txTrace)
11713  {
11714  fprintf (log, "\n" "--------------------------------------------------\n"
11715  "Стек вызовов TX:\n\n"
11716  "%s\n",
11717  txTrace);
11718  }
11719 
11720  #endif
11721 
11722  fprintf (log, "\n" "--------------------------------------------------\n"
11723  "%s\n\n"
11724  "--------------------------------------------------\n",
11725  _txAppInfo());
11726  fclose (log);
11727  break;
11728  }
11729  while (false);
11730 
11731  int ret = 0;
11732 
11733  if (!(options & noMsgBox))
11734  {
11736 
11737  PRINT_ ("\n" "Прервать программу?");
11738 
11739  ret = txMessageBox (what, ((options & isWarning)? "Предупреждение" :
11740  (options & isFatal)? "Фатальная ошибка" :
11741  "Ошибка в программе), MB_ICONSTOP | MB_SYSTEMMODAL | MB_YESNOCANCEL); } SetLastError (winErr); errno = crtErr; #if !defined (__CYGWIN__) _doserrno = dosErr; #endif if (((options & isFatal) && !IsDebuggerPresent()) || ret == IDYES) { txUnlock(); _txCleanup(); Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE); } #undef PRINT_ return what; } //----------------------------------------------------------------------------------------------------------------- #if defined (_MSC_VER) int _txOnErrorReport (int type, const char* text, int* ret) { assert (text); assert (ret); _txErrors++; unsigned restore = txGetConsoleAttr(); switch (type) { case _CRT_WARN: txSetConsoleAttr (FOREGROUND_LIGHTRED); break; case _CRT_ERROR: txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA); break; case _CRT_ASSERT: txSetConsoleAttr (FOREGROUND_YELLOW); break; default: break; } const char startReport[] = "Detected memory leaks!\n", endReport[] = "Object dump complete.\n"; if (strcmp (text, startReport) == 0) // Dirty, dirty hack. А что делать? { _txOnErrorReport (type, "\n", NULL); _txOnErrorReport (type, _TX_VERSION " - ERROR: ", NULL); _txOnErrorReport (type, "Внимание: Обнаружены утечки памяти! (Для поиска используйте _TX_ALLOC_BREAK.)\n", NULL); _txOnErrorReport (type, "\n", NULL); } size_t len = strlen (text); if (text [len-1] != '\n') txOutputDebugPrintf ("%s", text); else if (strcmp (text, endReport) != 0) txOutputDebugPrintf ("%s" "%s - ERROR: ", text, _TX_VERSION); else txOutputDebugPrintf ("%s\n", text); DWORD n = 0; HANDLE err = GetStdHandle (STD_ERROR_HANDLE); WriteFile (err, text, (DWORD) strlen (text), &n, NULL); txSetConsoleAttr (restore); if (*_txLogName) do { HANDLE log = CreateFile (_txLogName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (log == INVALID_HANDLE_VALUE) break; SetFilePointer (log, 0, NULL, FILE_END); WriteFile (log, text, (DWORD) strlen (text), &n, NULL); CloseHandle (log); break; } while (false); if (ret) *ret = 0; return (type == _CRT_WARN); } #endif //----------------------------------------------------------------------------------------------------------------- int txMessageBox (const char text[], const char header[], unsigned flags /*= MB_ICONINFORMATION | MB_OKCANCEL*/) { $5 static wchar_t textW [_TX_BIGBUFSIZE * sizeof (wchar_t)] = L"[NULL text]"; $ static wchar_t headerW [_TX_BUFSIZE * sizeof (wchar_t)] = L"[NULL header]"; if (text) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, text, -1, textW, sizearr (textW)) || memset (textW, 0, sizeof (textW)); } if (header) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, header, -1, headerW, sizearr (headerW)) || memset (headerW, 0, sizeof (headerW)); } $ HWND wnd = _txCanvas_Window; $ int ret = MessageBoxW ((wnd && IsWindowVisible (wnd))? wnd : _TX_CALL (Win32::GetConsoleWindow,()), textW, headerW, flags | MB_SETFOREGROUND | MB_TOPMOST | MB_OKCANCEL ); $ if (ret == IDCANCEL) { $ SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0); $ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT); } $ return ret; } //----------------------------------------------------------------------------------------------------------------- bool txGetAsyncKeyState (int key) { $1 HWND wnd = GetForegroundWindow(); return (GetAsyncKeyState (key) & 0x8000) && (wnd == txWindow() || wnd == Win32::GetConsoleWindow()); } //----------------------------------------------------------------------------------------------------------------- bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...) { $5 if (_TX_ARGUMENT_FAILED (format)) return false; $ va_list arg; va_start (arg, format); $ bool ok = true; #if defined (_WIN32_IE) && (_WIN32_IE >= 0x0500) $ NOTIFYICONDATA nid = { sizeof (nid) }; $ nid.uFlags = NIF_ICON | NIF_TIP | NIF_INFO; $ nid.hWnd = NULL; $ nid.uID = 1; $ nid.hIcon = _txCreateTXIcon (16); assert (nid.hIcon); $ strncpy_s (nid.szTip, sizeof (nid.szTip), "TXLib Information", sizeof (nid.szTip)); $ strncpy_s (nid.szInfoTitle, sizeof (nid.szInfoTitle), (title? title : "TXLib сообщает"), sizeof (nid.szInfoTitle) - 1); $ _tx_vsnprintf_s (nid.szInfo, sizeof (nid.szInfo), format, arg); $ nid.dwInfoFlags = flags; $ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification)\n", nid.szInfoTitle, nid.szInfo); $ ok &= !!Shell_NotifyIcon (NIM_ADD, (::NOTIFYICONDATA*) &nid); $ ok &= !!Shell_NotifyIcon (NIM_MODIFY, (::NOTIFYICONDATA*) &nid); $ if (nid.hIcon) DestroyIcon (nid.hIcon) asserted; #else $ char nid_szInfo[_TX_BUFSIZE] = ""; $ _tx_vsnprintf_s (nid_szInfo, sizeof (nid_szInfo), format, arg); $ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification - NOT displayed)\n", title, nid_szInfo); $ ok = false; $ (void)flags; (void)title; #endif $ va_end (arg); return ok; } //----------------------------------------------------------------------------------------------------------------- void _txTrace (const char file[], int line, const char func[], const char msg[] /*= NULL*/, ...) { unsigned id = GetCurrentThreadId(); const char marks[2][2][3] = {{"uU", "cC"}, {"mM", "??"}}; char mark = marks [id == _txMainThreadId] [id == _txCanvas_ThreadId] [(_txLoc::Cur.inTX > 0)]; char msgStr[_TX_BUFSIZE] = ""; if (msg) { va_list arg; va_start (arg, msg); _tx_vsnprintf_s (msgStr, sizeof (msgStr) - 1, msg, arg); va_end (arg); } txOutputDebugPrintf ("%s - 0x%p %c%c%c%c%c%d [%c] - %-*s (%5d) " "|%*s%s" "%s%s\n", _TX_VERSION, &_txInitialized, "cC"[_txConsole], "mM"[_txMain], "dD"[_txIsDll], "rR"[_txRunning], "eE"[_txExit], _txLoc::Cur.trace, mark, (int) sizeof (__FILE__) - 1, (file? file : "(NULL file)"), line, 2 * (_txLoc::Cur.inTX - 1) * !!func, "", (func? func : ""), ((*msgStr && func)? ": " : ""), msgStr); } //----------------------------------------------------------------------------------------------------------------- int txOutputDebugPrintf (const char format[], ...) { if (!format) return 0; enum { msgbox = 1, print = 2, compr = 4 }; int options = 0; for (; format && *format; format++) { if (*format == '\a') options |= msgbox; else if (*format == '\f') options |= print; else if (*format == '\r') options |= compr; else break; } char text[_TX_BIGBUFSIZE] = ""; va_list arg; va_start (arg, format); int n = (int) _tx_vsnprintf_s (text, sizeof (text) - 1-1, format, arg); va_end (arg); struct __ { static int trimSpaces (char str[]) { char *dst = str, *src = str; for (char d = ' '; d; src++) if (isspace ((unsigned char)(*src))) { if (d != ' ') *dst++ = d = ' '; } else *dst++ = d = *src; return (int) (dst - str - 1); }}; if (options & compr) n = __::trimSpaces (text); OutputDebugString (text); if (options & print) fprintf (stderr, "%s", text); if (options & msgbox) txMessageBox (text, "Оказывается, что", MB_ICONEXCLAMATION); return n; } //----------------------------------------------------------------------------------------------------------------- intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) { if (!format) return 0; va_list arg; va_start (arg, format); intptr_t ret = _tx_vsnprintf_s (stream, size, format, arg); va_end (arg); return ret; } //----------------------------------------------------------------------------------------------------------------- intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg) { if (!stream || !format) return 0; #if defined (_TRUNCATE) intptr_t ret = _vsnprintf_s (stream, size, _TRUNCATE, format, arg); #else intptr_t ret = _vsnprintf (stream, size, format, arg); #endif if (ret < 0 && size >= 4) { const char ellipsis[] = "..."; size_t szEllipsis = sizeof (ellipsis) - 1; strncpy_s (stream + size - szEllipsis, szEllipsis+1, ellipsis, szEllipsis); } return (ret >= 0)? ret : size; } //----------------------------------------------------------------------------------------------------------------- #if defined (__CYGWIN__) int _getch() { termios oldattr = {}; tcgetattr (STDIN_FILENO, &oldattr); termios newattr = oldattr; newattr.c_lflag &= ~(ICANON | ECHO); tcsetattr (STDIN_FILENO, TCSANOW, &newattr); int ch = getchar(); tcsetattr (STDIN_FILENO, TCSANOW, &oldattr); return ch; } //----------------------------------------------------------------------------------------------------------------- int _putch (int ch) { termios old = {}; tcgetattr (STDOUT_FILENO, &old); termios cur = old; cur.c_lflag &= ~ICANON; cur.c_lflag |= ECHO; tcsetattr (STDOUT_FILENO, TCSANOW, &cur); putchar (ch); tcsetattr (STDOUT_FILENO, TCSANOW, &old); return ch; } //----------------------------------------------------------------------------------------------------------------- int _kbhit() { termios old = {}; tcgetattr (STDIN_FILENO, &old); termios cur = old; cur.c_lflag &= ~(ICANON | ECHO); cur.c_cc[VMIN] = 1; cur.c_cc[VTIME] = 0; tcsetattr (STDIN_FILENO, TCSAFLUSH, &cur); fd_set fd = {}; FD_SET (STDIN_FILENO, &fd); timeval tv = {}; int res = select (STDIN_FILENO + 1, &fd, NULL, NULL, &tv) && FD_ISSET (STDIN_FILENO, &fd); tcsetattr (STDIN_FILENO, TCSAFLUSH, &old); return res; } #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Information //----------------------------------------------------------------------------------------------------------------- const char* txGetModuleFileName (bool fileNameOnly /*= true*/) { static char name[MAX_PATH] = ""; if (!*name) { if (!GetModuleFileName (NULL, name, sizeof (name) - 1)) *name = 0; char* ext = strrchr (name, '.'); if (ext) _strlwr_s (ext, sizeof (name) - 1 - (ext - name)); } assert (*name); if (fileNameOnly) return name; static char fullName[MAX_PATH] = ""; strncpy_s (fullName, sizeof (fullName), name, sizeof (fullName) - 1); char* title = strrchr (fullName, '\\'); if (!title) title = fullName; char* ext = strrchr (fullName, '.'); if (!ext) ext = fullName + strlen (fullName); size_t sz = sizeof (fullName) - (ext - fullName); strncpy_s (ext, sz-1, " - TXLib", sz); return title + 1; } //----------------------------------------------------------------------------------------------------------------- const char* _txAppInfo() { $1 time_t timeT = time (NULL) - clock()/CLOCKS_PER_SEC; char timeS[32] = ""; ctime_s (timeS, sizeof (timeS), &timeT); static char text[_TX_BUFSIZE] = ""; char cwd [MAX_PATH] = ""; _tx_snprintf_s (text, sizeof (text) - 1, "Developed with:\n\n" "The Dumb Artist Library (TX Library)\n" _TX_VERSION "\n" _TX_AUTHOR "\n" "See license on: http://txlib.ru\n\n" "TXLib file:" "\t" __FILE__ "\n" "Compiled:" "\t" __DATE__ " " __TIME__ ", " __TX_COMPILER__ ", %s, %d-bit, " _TX_BUILDMODE "\n" "Started:" "\t" "%.6s %.4s %.8s\n\n" "Run file:" "\t" "%s\n" "Directory:" "\t" "%s", #if defined (_MSC_VER) "MSVC Runtime", #elif defined (__CYGWIN__) "Cygwin Runtime", #elif defined (_GCC_VER) && defined (_WIN64) __mingw_get_crt_info(), #else "MinGW Runtime " TX_QUOTE (__MINGW32_MAJOR_VERSION) "." TX_QUOTE (__MINGW32_MINOR_VERSION), #endif (sizeof (void*) == sizeof (DWORD))? 32 : 64, timeS + 4, timeS + 20, timeS + 11, // These offsets are ANSI standardized txGetModuleFileName(), _getcwd (cwd, sizeof (cwd) - 1)); return text; } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib API implementation // Реализация TXLib API //================================================================================================================= inline const char* txVersion() { return _TX_VERSION; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txVersionNumber() { return _TX_VER; } //----------------------------------------------------------------------------------------------------------------- inline HWND txWindow() { $0 return _txCanvas_Window; } //----------------------------------------------------------------------------------------------------------------- inline HDC& txDC() { $0 return _txCanvas_BackBuf[0]; } //----------------------------------------------------------------------------------------------------------------- inline RGBQUAD* txVideoMemory() { return _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- POINT txGetExtent (HDC dc /*= txDC()*/) { $0 static POINT err = {-1, -1}; if (!dc) {$ POINT screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; return screen; }; if (_TX_DEFAULT_HDC_FAILED (dc)) {$ return err; } $ BITMAP bmap = {}; $ txGDI (Win32::GetObject (Win32::GetCurrentObject (dc, OBJ_BITMAP), sizeof (bmap), &bmap), dc) asserted; $ POINT size = { bmap.bmWidth, bmap.bmHeight }; $ return size; } //----------------------------------------------------------------------------------------------------------------- int txGetExtentX (HDC dc /*= txDC()*/) { return txGetExtent (dc) .x; } //----------------------------------------------------------------------------------------------------------------- int txGetExtentY (HDC dc /*= txDC()*/) { return txGetExtent (dc) .y; } //----------------------------------------------------------------------------------------------------------------- bool txDestroyWindow (HWND wnd /*= txWindow()*/) { $1 if (!wnd || !txWindow()) return false; $ if (wnd != txWindow()) { $ return !!SendNotifyMessage (txWindow(), _TX_WM_DESTROYWND, 0, (LPARAM) wnd); } $ if (SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0) == 0) return false; $ if (_txMain) { $ txNotifyIcon (NIIF_WARNING, NULL, "\n" "Очень, очень плохо завершать программу через txDestroyWindow().\n\n" "Возвращайтесь через main(), там вам будут рады.\n"); $ Sleep (_TX_TIMEOUT); } $ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT); $ return _txCanvas_Window == NULL; } //----------------------------------------------------------------------------------------------------------------- HPEN txSetColor (COLORREF color, double thickness /*= 1*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HPEN pen = Win32::CreatePen ((color == TX_TRANSPARENT? PS_NULL : PS_SOLID), ROUND (thickness), color); $ if (!pen) return (HPEN) NULL; $ if (!_txBuffer_Select (pen, dc)) { $ Win32::DeleteObject (pen); $ return (HPEN) NULL; } $ if (txGDI (Win32::SetTextColor (dc, color), dc) == CLR_INVALID) {$ return (HPEN) NULL; } $ return pen; } //----------------------------------------------------------------------------------------------------------------- COLORREF txColor (double red, double green, double blue) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)); $ return txSetColor (color)? color : CLR_INVALID; } //----------------------------------------------------------------------------------------------------------------- COLORREF txGetColor (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_PEN)), dc); $ assert (obj); if (!obj) return CLR_INVALID; $ union { EXTLOGPEN extLogPen; LOGPEN LogPen; } buf = {}; $ int size = Win32::GetObject (obj, 0, NULL); $ Win32::GetObject (obj, sizeof (buf), &buf) asserted; $ return (size == sizeof (LOGPEN))? buf.LogPen.lopnColor : buf.extLogPen.elpColor; } //----------------------------------------------------------------------------------------------------------------- HBRUSH txSetFillColor (COLORREF color, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HBRUSH brush = (color == TX_TRANSPARENT)? (HBRUSH) Win32::GetStockObject (HOLLOW_BRUSH) : Win32::CreateSolidBrush (color); $ return (_txBuffer_Select (brush, dc))? brush : (Win32::DeleteObject (brush), (HBRUSH) NULL); } //----------------------------------------------------------------------------------------------------------------- COLORREF txFillColor (double red, double green, double blue) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)); $ return txSetFillColor (color)? color : CLR_INVALID; } //----------------------------------------------------------------------------------------------------------------- COLORREF txGetFillColor (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_BRUSH)), dc); $ assert (obj); if (!obj) return CLR_INVALID; $ LOGBRUSH buf = {}; $ txGDI ((Win32::GetObject (obj, sizeof (buf), &buf)), dc) asserted; $ return buf.lbColor; } //----------------------------------------------------------------------------------------------------------------- bool txClear (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ return txGDI (!!(Win32::PatBlt (dc, 0, 0, size.x, size.y, PATCOPY)), dc); } //----------------------------------------------------------------------------------------------------------------- inline bool txSetPixel (double x, double y, COLORREF color, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ txGDI ((Win32::SetPixel (dc, ROUND (x), ROUND (y), color)), dc); $ return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txPixel (double x, double y, double red, double green, double blue, HDC dc /*= txDC()*/) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ return txSetPixel (x, y, RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)), dc); } //----------------------------------------------------------------------------------------------------------------- inline COLORREF txGetPixel (double x, double y, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ return txGDI ((Win32::GetPixel (dc, ROUND (x), ROUND (y))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txLine (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ bool ok = txGDI ((Win32::MoveToEx (dc, ROUND (x0), ROUND (y0), NULL)), dc); $ ok &= txGDI ((Win32::LineTo (dc, ROUND (x1), ROUND (y1) )), dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txRectangle (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::Rectangle (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txPolygon (const POINT points[], int numPoints, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (points)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI (!!(Win32::Polygon (dc, points, numPoints)), dc); } //----------------------------------------------------------------------------------------------------------------- bool txEllipse (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::Ellipse (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txCircle (double x, double y, double r) { $1 return txEllipse (x-r, y-r, x+r, y+r); } //----------------------------------------------------------------------------------------------------------------- bool txArc (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Arc (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txPie (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Pie (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txChord (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Chord (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txFloodFill (double x, double y, COLORREF color /*= TX_TRANSPARENT*/, DWORD mode /*= FLOODFILLSURFACE*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ if (color == TX_TRANSPARENT) color = txGetPixel (x, y, dc); $ return txGDI (!!(Win32::ExtFloodFill (dc, ROUND (x), ROUND (y), color, mode)), dc); } //----------------------------------------------------------------------------------------------------------------- bool txTextOut (double x, double y, const char text[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (text)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ int len = (int) strlen (text); $ bool ok = txGDI (!!(Win32::TextOut (dc, ROUND (x), ROUND (y), text, len)), dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txDrawText (double x0, double y0, double x1, double y1, const char text[], unsigned format /*= DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS*/, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (text)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; #if !defined (NDEBUG) $ if (x0 > x1) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Параметр x0 = %lg больше, чем x1 = %lg. Текст выводиться не будет.", x0, x1); } $ if (y0 > y1) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Параметр y0 = %lg больше, чем y1 = %lg. Текст выводиться не будет.", y0, y1); } #endif $ RECT r = { ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1) }; $ if (!strchr (text, '\n')) format |= DT_SINGLELINE; $ unsigned prev = txSetTextAlign (TA_LEFT | TA_TOP | TA_NOUPDATECP, dc); $ bool ok = false; $ if (Win32::DrawText) { $ ok = !!txGDI ((Win32::DrawText (dc, text, -1, &r, format)), dc); $ Win32::GetPixel (dc, 0, 0); $ ok = true; } else { $ txTextOut ((x0 + x1) / 2, (y0 + y1) / 2, text); $ ok = false; } $ txSetTextAlign (prev, dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- HFONT txSelectFont (const char name[], double sizeY, double sizeX /*= -1*/, int bold /*= FW_DONTCARE*/, bool italic /*= false*/, bool underline /*= false*/, bool strikeout /*= false*/, double angle /*= 0*/, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (name)) return NULL; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HFONT font = txFontExist (name)? Win32::CreateFont (ROUND (sizeY), ROUND ((sizeX >= 0)? sizeX : sizeY/3), ROUND (angle*10), 0, bold, italic, underline, strikeout, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, name) : (HFONT) Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, dc); $ return font; } //----------------------------------------------------------------------------------------------------------------- SIZE txGetTextExtent (const char text[], HDC dc /*= txDC()*/) { $1 SIZE size = {-1, -1}; $ if (_TX_ARGUMENT_FAILED (text)) return size; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return size; $ size_t len = strlen (text); $ txGDI ((Win32::GetTextExtentPoint32 (dc, text, (int) len, &size)), dc) asserted; $ return size; } //----------------------------------------------------------------------------------------------------------------- int txGetTextExtentX (const char text[], HDC dc /*= txDC()*/) { $1 return txGetTextExtent (text, dc) .cx; } //----------------------------------------------------------------------------------------------------------------- int txGetTextExtentY (const char text[], HDC dc /*= txDC()*/) { $1 return txGetTextExtent (text, dc) .cy; } //----------------------------------------------------------------------------------------------------------------- unsigned txSetTextAlign (unsigned align /*= TA_CENTER | TA_BASELINE*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::SetTextAlign (dc, align)), dc); } //----------------------------------------------------------------------------------------------------------------- LOGFONT* txFontExist (const char name[]) { $1 if (_TX_ARGUMENT_FAILED (name)) return NULL; $ static LOGFONT font = {}; $ font.lfCharSet = DEFAULT_CHARSET; $ strncpy_s (font.lfFaceName, sizeof (font.lfFaceName), name, sizeof (font.lfFaceName) - 1); $ struct tools { static int CALLBACK enumFonts (const LOGFONT* fnt, const TEXTMETRIC*, DWORD, LPARAM data) { $ if (_TX_ARGUMENT_FAILED (fnt)) return 0; $ if (_TX_ARGUMENT_FAILED (data)) return 0; #ifndef __STRICT_ANSI__ $ return _strnicmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE); #else $ return strncmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE); #endif } }; $ return txGDI ((Win32::EnumFontFamiliesEx (NULL, &font, tools::enumFonts, (LPARAM) &font, 0)), NULL) == 0? &font : NULL; } //----------------------------------------------------------------------------------------------------------------- bool txSelectObject (HGDIOBJ obj, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (obj)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return _txBuffer_Select (obj, dc); } //----------------------------------------------------------------------------------------------------------------- HDC txCreateCompatibleDC (double sizeX, double sizeY, HBITMAP bitmap /*= NULL*/) { $1 POINT size = { ROUND (sizeX), ROUND (sizeY) }; $ HDC dc = _txBuffer_Create (NULL, &size, bitmap); $ assert (dc); if (!dc) return NULL; $ txSetDefaults (dc); $ if (!_txCanvas_UserDCs) return dc; $ txAutoLock _lock; $ _txCanvas_UserDCs->push_back (dc); $ if (_txCanvas_UserDCs->size() >= _TX_BUFSIZE) {$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загрузили уже %d HDC, системе может стать плохо.", (int) _txCanvas_UserDCs->size()); } $ return dc; } //----------------------------------------------------------------------------------------------------------------- HDC txCreateDIBSection (double sizeX, double sizeY, RGBQUAD** pixels /*= NULL*/) { $1 RGBQUAD* buf = NULL; $ if (!pixels) pixels = &buf; $ BITMAPINFO info = {{ sizeof (info), ROUND (sizeX), ROUND (sizeY), 1, WORD (sizeof (RGBQUAD) * 8), BI_RGB }}; $ HDC dc = txCreateCompatibleDC (0, 0, Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (void**) pixels, NULL, 0)); $ RGBQUAD black = { 0, 0, 0, 255 }; $ for (int i = 0; i < sizeX * sizeY; i++) (*pixels)[i] = black; $ return dc; } //----------------------------------------------------------------------------------------------------------------- HDC txCreateDIBSection (double sizeX, double sizeY, COLORREF** pixels) { $1 return txCreateDIBSection (sizeX, sizeY, (RGBQUAD**) pixels); } //----------------------------------------------------------------------------------------------------------------- HDC txLoadImage (const char filename[], unsigned imageFlags /*= IMAGE_BITMAP*/, unsigned loadFlags /*= LR_LOADFROMFILE*/) { $1 if (_TX_ARGUMENT_FAILED (filename && *filename)) return NULL; $ HBITMAP image = (HBITMAP) Win32::LoadImage ((loadFlags & LR_LOADFROMFILE)? NULL : GetModuleHandle (NULL), filename, imageFlags, 0, 0, loadFlags); $ if (!image) return NULL; $ HDC dc = txCreateCompatibleDC (0, 0, image); $ if (!(loadFlags & LR_LOADFROMFILE)) return dc; $ static std::map <std::string, unsigned> loadTimes; $ std::string file = filename; $ unsigned time = GetTickCount(); $ if ((long) (time - loadTimes [file]) < _TX_TIMEOUT) {$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загружаете \"%s\" слишком часто, программа будет тормозить.", filename); } $ loadTimes [file] = time; $ return dc; } //----------------------------------------------------------------------------------------------------------------- bool txDeleteDC (HDC* pdc) { $1 if (_TX_ARGUMENT_FAILED (pdc)) return false; $ HDC dc = *pdc; $ bool ok = _txBuffer_Delete (pdc); $ if (!ok) return false; $ if (!_txCanvas_UserDCs) return ok; $ txAutoLock _lock; $ std::vector <HDC> ::iterator it = std::find (_txCanvas_UserDCs->begin(), _txCanvas_UserDCs->end(), dc); $ if (it != _txCanvas_UserDCs->end()) { std::swap (*it, _txCanvas_UserDCs->back()); _txCanvas_UserDCs->pop_back(); } $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txDeleteDC (HDC dc) { $1 return txDeleteDC (&dc); } //----------------------------------------------------------------------------------------------------------------- bool txBitBlt (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, unsigned operation /*= SRCCOPY*/) { $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; $ return txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), operation)), destImage); } //----------------------------------------------------------------------------------------------------------------- inline bool txBitBlt (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txBitBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource); } //----------------------------------------------------------------------------------------------------------------- bool txTransparentBlt (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, COLORREF transColor /*= TX_BLACK*/) { $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; #if !defined (NDEBUG) $ if (!(0 <= xSource && xSource + width <= size.x && 0 <= ySource && ySource + height <= size.y)) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, " "функция txTransparentBlt() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y); } #endif $ bool ok = (Win32::TransparentBlt != NULL); $ if (ok) { $ ok &= txGDI (!!(Win32::TransparentBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), transColor)), destImage); } else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); } return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txTransparentBlt (double xDest, double yDest, HDC sourceImage, COLORREF transColor /*= TX_BLACK*/, double xSource /*= 0*/, double ySource /*= 0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txTransparentBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, transColor); } //----------------------------------------------------------------------------------------------------------------- bool txAlphaBlend (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { // Это проверки того, правильные ли HDC вы передали в функцию. // Не бойтесь долларов - <s>это не запрещенная валюта</s> это макросы для отладки TXLib'а. $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; // Это автоматическое определение размеров картинки (точнее, HDC источника - source) с помощью txGetExtent(). $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; // Это проверка того, что картинка (или ее часть) правильно попадает в окно (точнее, HDC приемника - destination, dest). // Если она "вылезает" из окна в любую сторону, то Win32::AlphaBlend не будет работать. Эта проверка происходит только // в режиме отладки (когда не задан макрос NDEBUG - No Debugging, без отладки). #if !defined (NDEBUG) $ if (!(0 <= xSource && xSource + width <= size.x && 0 <= ySource && ySource + height <= size.y)) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, " "функция txAlphaBlend() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y); } #endif // Это на случай, если параметр alpha вылезает за диапазон [0..1]. $ if (alpha < 0) alpha = 0; $ if (alpha > 1) alpha = 1; // Об этом см. ниже. $ BITMAP bmap = { 0, 0, 0, 0, 0, 24 }; $ bool ok = !!Win32::GetObject (txGDI ((Win32::GetCurrentObject (sourceImage, OBJ_BITMAP)), sourceImage), sizeof (bmap), &bmap); // // * Структура BLENDFUNCTION * // // Эта структура определяет, как цвета пикселей окна (точнее, source DC) и картинки смешиваются при рисовании с учетом // прозрачности. // // Параметры смешивания помещаются в группу из переменных - так называемую структуру. Переменные, входящие в структуру, // называются компонентами структуры. // // В этой структуре более всех важен ее третий компонент (см. ниже), задающий прозрачность картинки. // Если он равен нулю - то картинка будет считаться полностью прозрачной и вызов Win32::AlphaBlend ничего не нарисует. // Если он равен 255 - то картинка скопируется в окно полностью, без эффекта прозрачности. // // С помощью четвертого компонента структуры система рисования Windows (Win32 GDI) узнает, надо ли учитывать альфа-канал // в копируемой картинке или его надо игнорировать. Если в картинке фактически есть правильно построенный альфа-канал // (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу // AC_SRC_ALPHA. // // Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0. // // В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения // функции txAlphaBlend(). // // Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать // альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы // породите невнятный паленый код и безнадежно испортите себе карму. :(( // // На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо. // // ____1____ 2 ___________3___________ _______________________4________________________ // / \ | / \ / \. $ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) }; // // * Вызов стандартной функции Win32::AlphaBlend() * // // Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend() // вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите // "Windows AlphaBlend function" и почитайте про ее параметры. // // Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не // совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s> // это сделано для упрощения вызова функции txAlphaBlend(). $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<-- { // // vvvvv vvvvvv // <<-- $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<== sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<== destImage); // ^^^^^ ^^^^^^ // <<-- } // ||||| |||||| // <<-- // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<-- else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); $ ok = false; } // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки), MB_ICONSTOP | MB_SYSTEMMODAL | MB_YESNOCANCEL);
11742  }
11743 
11744  SetLastError (winErr);
11745 
11746  errno = crtErr;
11747 
11748  #if !defined (__CYGWIN__)
11749  _doserrno = dosErr;
11750  #endif
11751 
11752  if (((options & isFatal) && !IsDebuggerPresent()) || ret == IDYES)
11753  {
11754  txUnlock();
11755  _txCleanup();
11756  Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
11757  }
11758 
11759  #undef PRINT_
11760 
11761  return what;
11762  }
11763 
11764 //-----------------------------------------------------------------------------------------------------------------
11765 
11766 #if defined (_MSC_VER)
11767 
11768 int _txOnErrorReport (int type, const char* text, int* ret)
11769  {
11770  assert (text);
11771  assert (ret);
11772 
11773  _txErrors++;
11774 
11775  unsigned restore = txGetConsoleAttr();
11776 
11777  switch (type)
11778  {
11779  case _CRT_WARN: txSetConsoleAttr (FOREGROUND_LIGHTRED); break;
11780  case _CRT_ERROR: txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA); break;
11781  case _CRT_ASSERT: txSetConsoleAttr (FOREGROUND_YELLOW); break;
11782  default: break;
11783  }
11784 
11785  const char startReport[] = "Detected memory leaks!\n",
11786  endReport[] = "Object dump complete.\n";
11787 
11788  if (strcmp (text, startReport) == 0) // Dirty, dirty hack. А что делать?
11789  {
11790  _txOnErrorReport (type, "\n", NULL);
11791  _txOnErrorReport (type, _TX_VERSION " - ERROR: ", NULL);
11792  _txOnErrorReport (type, "Внимание: Обнаружены утечки памяти! (Для поиска используйте _TX_ALLOC_BREAK.)\n", NULL);
11793  _txOnErrorReport (type, "\n", NULL);
11794  }
11795 
11796  size_t len = strlen (text);
11797  if (text [len-1] != '\n') txOutputDebugPrintf ("%s", text);
11798  else if (strcmp (text, endReport) != 0) txOutputDebugPrintf ("%s" "%s - ERROR: ", text, _TX_VERSION);
11799  else txOutputDebugPrintf ("%s\n", text);
11800 
11801  DWORD n = 0;
11802  HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
11803  WriteFile (err, text, (DWORD) strlen (text), &n, NULL);
11804 
11805  txSetConsoleAttr (restore);
11806 
11807  if (*_txLogName) do
11808  {
11809  HANDLE log = CreateFile (_txLogName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
11810  if (log == INVALID_HANDLE_VALUE) break;
11811 
11812  SetFilePointer (log, 0, NULL, FILE_END);
11813  WriteFile (log, text, (DWORD) strlen (text), &n, NULL);
11814 
11815  CloseHandle (log);
11816  break;
11817  }
11818  while (false);
11819 
11820  if (ret) *ret = 0;
11821 
11822  return (type == _CRT_WARN);
11823  }
11824 
11825 #endif
11826 
11827 //-----------------------------------------------------------------------------------------------------------------
11828 
11829 int txMessageBox (const char text[], const char header[], unsigned flags /*= MB_ICONINFORMATION | MB_OKCANCEL*/)
11830  {
11831 $5 static wchar_t textW [_TX_BIGBUFSIZE * sizeof (wchar_t)] = L"[NULL text]";
11832 $ static wchar_t headerW [_TX_BUFSIZE * sizeof (wchar_t)] = L"[NULL header]";
11833 
11834  if (text) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, text, -1, textW, sizearr (textW)) || memset (textW, 0, sizeof (textW)); }
11835  if (header) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, header, -1, headerW, sizearr (headerW)) || memset (headerW, 0, sizeof (headerW)); }
11836 
11837 $ HWND wnd = _txCanvas_Window;
11838 $ int ret = MessageBoxW ((wnd && IsWindowVisible (wnd))? wnd : _TX_CALL (Win32::GetConsoleWindow,()),
11839  textW, headerW, flags | MB_SETFOREGROUND | MB_TOPMOST | MB_OKCANCEL );
11840 $ if (ret == IDCANCEL)
11841  {
11842 $ SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0);
11843 $ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT);
11844  }
11845 
11846 $ return ret;
11847  }
11848 
11849 //-----------------------------------------------------------------------------------------------------------------
11850 
11851 bool txGetAsyncKeyState (int key)
11852  {
11853 $1 HWND wnd = GetForegroundWindow();
11854 
11855  return (GetAsyncKeyState (key) & 0x8000) &&
11856  (wnd == txWindow() || wnd == Win32::GetConsoleWindow());
11857  }
11858 
11859 //-----------------------------------------------------------------------------------------------------------------
11860 
11861 bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...)
11862  {
11863 $5 if (_TX_ARGUMENT_FAILED (format)) return false;
11864 
11865 $ va_list arg; va_start (arg, format);
11866 $ bool ok = true;
11867 
11868  #if defined (_WIN32_IE) && (_WIN32_IE >= 0x0500)
11869 
11870 $ NOTIFYICONDATA nid = { sizeof (nid) };
11871 
11872 $ nid.uFlags = NIF_ICON | NIF_TIP | NIF_INFO;
11873 $ nid.hWnd = NULL;
11874 $ nid.uID = 1;
11875 $ nid.hIcon = _txCreateTXIcon (16); assert (nid.hIcon);
11876 $ strncpy_s (nid.szTip, sizeof (nid.szTip), "TXLib Information", sizeof (nid.szTip));
11877 $ strncpy_s (nid.szInfoTitle, sizeof (nid.szInfoTitle), (title? title : "TXLib сообщает"), sizeof (nid.szInfoTitle) - 1);
11878 $ _tx_vsnprintf_s (nid.szInfo, sizeof (nid.szInfo), format, arg);
11879 $ nid.dwInfoFlags = flags;
11880 
11881 $ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification)\n", nid.szInfoTitle, nid.szInfo);
11882 
11883 $ ok &= !!Shell_NotifyIcon (NIM_ADD, (::NOTIFYICONDATA*) &nid);
11884 $ ok &= !!Shell_NotifyIcon (NIM_MODIFY, (::NOTIFYICONDATA*) &nid);
11885 
11886 $ if (nid.hIcon) DestroyIcon (nid.hIcon) asserted;
11887 
11888  #else
11889 
11890 $ char nid_szInfo[_TX_BUFSIZE] = "";
11891 $ _tx_vsnprintf_s (nid_szInfo, sizeof (nid_szInfo), format, arg);
11892 $ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification - NOT displayed)\n", title, nid_szInfo);
11893 $ ok = false;
11894 
11895 $ (void)flags; (void)title;
11896 
11897  #endif
11898 
11899 $ va_end (arg);
11900  return ok;
11901  }
11902 
11903 //-----------------------------------------------------------------------------------------------------------------
11904 
11905 void _txTrace (const char file[], int line, const char func[], const char msg[] /*= NULL*/, ...)
11906  {
11907  unsigned id = GetCurrentThreadId();
11908 
11909  const char marks[2][2][3] = {{"uU", "cC"}, {"mM", "??"}};
11910 
11911  char mark = marks [id == _txMainThreadId] [id == _txCanvas_ThreadId] [(_txLoc::Cur.inTX > 0)];
11912 
11913  char msgStr[_TX_BUFSIZE] = "";
11914  if (msg)
11915  {
11916  va_list arg; va_start (arg, msg);
11917  _tx_vsnprintf_s (msgStr, sizeof (msgStr) - 1, msg, arg);
11918  va_end (arg);
11919  }
11920 
11921  txOutputDebugPrintf ("%s - 0x%p %c%c%c%c%c%d [%c] - %-*s (%5d) " "|%*s%s" "%s%s\n",
11922 
11923  _TX_VERSION, &_txInitialized,
11924 
11925  "cC"[_txConsole], "mM"[_txMain], "dD"[_txIsDll], "rR"[_txRunning], "eE"[_txExit],
11926  _txLoc::Cur.trace, mark,
11927 
11928  (int) sizeof (__FILE__) - 1, (file? file : "(NULL file)"), line,
11929  2 * (_txLoc::Cur.inTX - 1) * !!func, "", (func? func : ""),
11930 
11931  ((*msgStr && func)? ": " : ""), msgStr);
11932  }
11933 
11934 //-----------------------------------------------------------------------------------------------------------------
11935 
11936 int txOutputDebugPrintf (const char format[], ...)
11937  {
11938  if (!format) return 0;
11939 
11940  enum { msgbox = 1, print = 2, compr = 4 };
11941  int options = 0;
11942 
11943  for (; format && *format; format++)
11944  {
11945  if (*format == '\a') options |= msgbox;
11946  else if (*format == '\f') options |= print;
11947  else if (*format == '\r') options |= compr;
11948  else break;
11949  }
11950 
11951  char text[_TX_BIGBUFSIZE] = "";
11952 
11953  va_list arg; va_start (arg, format);
11954  int n = (int) _tx_vsnprintf_s (text, sizeof (text) - 1-1, format, arg);
11955  va_end (arg);
11956 
11957  struct __ { static int trimSpaces (char str[])
11958  {
11959  char *dst = str, *src = str;
11960 
11961  for (char d = ' '; d; src++)
11962  if (isspace ((unsigned char)(*src))) { if (d != ' ') *dst++ = d = ' '; }
11963  else *dst++ = d = *src;
11964 
11965  return (int) (dst - str - 1);
11966  }};
11967 
11968  if (options & compr) n = __::trimSpaces (text);
11969 
11970  OutputDebugString (text);
11971 
11972  if (options & print) fprintf (stderr, "%s", text);
11973 
11974  if (options & msgbox) txMessageBox (text, "Оказывается, что, MB_ICONEXCLAMATION); return n; } //----------------------------------------------------------------------------------------------------------------- intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...) { if (!format) return 0; va_list arg; va_start (arg, format); intptr_t ret = _tx_vsnprintf_s (stream, size, format, arg); va_end (arg); return ret; } //----------------------------------------------------------------------------------------------------------------- intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg) { if (!stream || !format) return 0; #if defined (_TRUNCATE) intptr_t ret = _vsnprintf_s (stream, size, _TRUNCATE, format, arg); #else intptr_t ret = _vsnprintf (stream, size, format, arg); #endif if (ret < 0 && size >= 4) { const char ellipsis[] = "..."; size_t szEllipsis = sizeof (ellipsis) - 1; strncpy_s (stream + size - szEllipsis, szEllipsis+1, ellipsis, szEllipsis); } return (ret >= 0)? ret : size; } //----------------------------------------------------------------------------------------------------------------- #if defined (__CYGWIN__) int _getch() { termios oldattr = {}; tcgetattr (STDIN_FILENO, &oldattr); termios newattr = oldattr; newattr.c_lflag &= ~(ICANON | ECHO); tcsetattr (STDIN_FILENO, TCSANOW, &newattr); int ch = getchar(); tcsetattr (STDIN_FILENO, TCSANOW, &oldattr); return ch; } //----------------------------------------------------------------------------------------------------------------- int _putch (int ch) { termios old = {}; tcgetattr (STDOUT_FILENO, &old); termios cur = old; cur.c_lflag &= ~ICANON; cur.c_lflag |= ECHO; tcsetattr (STDOUT_FILENO, TCSANOW, &cur); putchar (ch); tcsetattr (STDOUT_FILENO, TCSANOW, &old); return ch; } //----------------------------------------------------------------------------------------------------------------- int _kbhit() { termios old = {}; tcgetattr (STDIN_FILENO, &old); termios cur = old; cur.c_lflag &= ~(ICANON | ECHO); cur.c_cc[VMIN] = 1; cur.c_cc[VTIME] = 0; tcsetattr (STDIN_FILENO, TCSAFLUSH, &cur); fd_set fd = {}; FD_SET (STDIN_FILENO, &fd); timeval tv = {}; int res = select (STDIN_FILENO + 1, &fd, NULL, NULL, &tv) && FD_ISSET (STDIN_FILENO, &fd); tcsetattr (STDIN_FILENO, TCSAFLUSH, &old); return res; } #endif //} //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- //{ Information //----------------------------------------------------------------------------------------------------------------- const char* txGetModuleFileName (bool fileNameOnly /*= true*/) { static char name[MAX_PATH] = ""; if (!*name) { if (!GetModuleFileName (NULL, name, sizeof (name) - 1)) *name = 0; char* ext = strrchr (name, '.'); if (ext) _strlwr_s (ext, sizeof (name) - 1 - (ext - name)); } assert (*name); if (fileNameOnly) return name; static char fullName[MAX_PATH] = ""; strncpy_s (fullName, sizeof (fullName), name, sizeof (fullName) - 1); char* title = strrchr (fullName, '\\'); if (!title) title = fullName; char* ext = strrchr (fullName, '.'); if (!ext) ext = fullName + strlen (fullName); size_t sz = sizeof (fullName) - (ext - fullName); strncpy_s (ext, sz-1, " - TXLib", sz); return title + 1; } //----------------------------------------------------------------------------------------------------------------- const char* _txAppInfo() { $1 time_t timeT = time (NULL) - clock()/CLOCKS_PER_SEC; char timeS[32] = ""; ctime_s (timeS, sizeof (timeS), &timeT); static char text[_TX_BUFSIZE] = ""; char cwd [MAX_PATH] = ""; _tx_snprintf_s (text, sizeof (text) - 1, "Developed with:\n\n" "The Dumb Artist Library (TX Library)\n" _TX_VERSION "\n" _TX_AUTHOR "\n" "See license on: http://txlib.ru\n\n" "TXLib file:" "\t" __FILE__ "\n" "Compiled:" "\t" __DATE__ " " __TIME__ ", " __TX_COMPILER__ ", %s, %d-bit, " _TX_BUILDMODE "\n" "Started:" "\t" "%.6s %.4s %.8s\n\n" "Run file:" "\t" "%s\n" "Directory:" "\t" "%s", #if defined (_MSC_VER) "MSVC Runtime", #elif defined (__CYGWIN__) "Cygwin Runtime", #elif defined (_GCC_VER) && defined (_WIN64) __mingw_get_crt_info(), #else "MinGW Runtime " TX_QUOTE (__MINGW32_MAJOR_VERSION) "." TX_QUOTE (__MINGW32_MINOR_VERSION), #endif (sizeof (void*) == sizeof (DWORD))? 32 : 64, timeS + 4, timeS + 20, timeS + 11, // These offsets are ANSI standardized txGetModuleFileName(), _getcwd (cwd, sizeof (cwd) - 1)); return text; } //} //----------------------------------------------------------------------------------------------------------------- //! @} //} //================================================================================================================= //================================================================================================================= //{ TXLib API implementation // Реализация TXLib API //================================================================================================================= inline const char* txVersion() { return _TX_VERSION; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txVersionNumber() { return _TX_VER; } //----------------------------------------------------------------------------------------------------------------- inline HWND txWindow() { $0 return _txCanvas_Window; } //----------------------------------------------------------------------------------------------------------------- inline HDC& txDC() { $0 return _txCanvas_BackBuf[0]; } //----------------------------------------------------------------------------------------------------------------- inline RGBQUAD* txVideoMemory() { return _txCanvas_Pixels; } //----------------------------------------------------------------------------------------------------------------- POINT txGetExtent (HDC dc /*= txDC()*/) { $0 static POINT err = {-1, -1}; if (!dc) {$ POINT screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; return screen; }; if (_TX_DEFAULT_HDC_FAILED (dc)) {$ return err; } $ BITMAP bmap = {}; $ txGDI (Win32::GetObject (Win32::GetCurrentObject (dc, OBJ_BITMAP), sizeof (bmap), &bmap), dc) asserted; $ POINT size = { bmap.bmWidth, bmap.bmHeight }; $ return size; } //----------------------------------------------------------------------------------------------------------------- int txGetExtentX (HDC dc /*= txDC()*/) { return txGetExtent (dc) .x; } //----------------------------------------------------------------------------------------------------------------- int txGetExtentY (HDC dc /*= txDC()*/) { return txGetExtent (dc) .y; } //----------------------------------------------------------------------------------------------------------------- bool txDestroyWindow (HWND wnd /*= txWindow()*/) { $1 if (!wnd || !txWindow()) return false; $ if (wnd != txWindow()) { $ return !!SendNotifyMessage (txWindow(), _TX_WM_DESTROYWND, 0, (LPARAM) wnd); } $ if (SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0) == 0) return false; $ if (_txMain) { $ txNotifyIcon (NIIF_WARNING, NULL, "\n" "Очень, очень плохо завершать программу через txDestroyWindow().\n\n" "Возвращайтесь через main(), там вам будут рады.\n"); $ Sleep (_TX_TIMEOUT); } $ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT); $ return _txCanvas_Window == NULL; } //----------------------------------------------------------------------------------------------------------------- HPEN txSetColor (COLORREF color, double thickness /*= 1*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HPEN pen = Win32::CreatePen ((color == TX_TRANSPARENT? PS_NULL : PS_SOLID), ROUND (thickness), color); $ if (!pen) return (HPEN) NULL; $ if (!_txBuffer_Select (pen, dc)) { $ Win32::DeleteObject (pen); $ return (HPEN) NULL; } $ if (txGDI (Win32::SetTextColor (dc, color), dc) == CLR_INVALID) {$ return (HPEN) NULL; } $ return pen; } //----------------------------------------------------------------------------------------------------------------- COLORREF txColor (double red, double green, double blue) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)); $ return txSetColor (color)? color : CLR_INVALID; } //----------------------------------------------------------------------------------------------------------------- COLORREF txGetColor (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_PEN)), dc); $ assert (obj); if (!obj) return CLR_INVALID; $ union { EXTLOGPEN extLogPen; LOGPEN LogPen; } buf = {}; $ int size = Win32::GetObject (obj, 0, NULL); $ Win32::GetObject (obj, sizeof (buf), &buf) asserted; $ return (size == sizeof (LOGPEN))? buf.LogPen.lopnColor : buf.extLogPen.elpColor; } //----------------------------------------------------------------------------------------------------------------- HBRUSH txSetFillColor (COLORREF color, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HBRUSH brush = (color == TX_TRANSPARENT)? (HBRUSH) Win32::GetStockObject (HOLLOW_BRUSH) : Win32::CreateSolidBrush (color); $ return (_txBuffer_Select (brush, dc))? brush : (Win32::DeleteObject (brush), (HBRUSH) NULL); } //----------------------------------------------------------------------------------------------------------------- COLORREF txFillColor (double red, double green, double blue) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)); $ return txSetFillColor (color)? color : CLR_INVALID; } //----------------------------------------------------------------------------------------------------------------- COLORREF txGetFillColor (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_BRUSH)), dc); $ assert (obj); if (!obj) return CLR_INVALID; $ LOGBRUSH buf = {}; $ txGDI ((Win32::GetObject (obj, sizeof (buf), &buf)), dc) asserted; $ return buf.lbColor; } //----------------------------------------------------------------------------------------------------------------- bool txClear (HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ return txGDI (!!(Win32::PatBlt (dc, 0, 0, size.x, size.y, PATCOPY)), dc); } //----------------------------------------------------------------------------------------------------------------- inline bool txSetPixel (double x, double y, COLORREF color, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ txGDI ((Win32::SetPixel (dc, ROUND (x), ROUND (y), color)), dc); $ return true; } //----------------------------------------------------------------------------------------------------------------- inline bool txPixel (double x, double y, double red, double green, double blue, HDC dc /*= txDC()*/) { $1 if (red > 1) red = 1; if (red < 0) red = 0; $ if (green > 1) green = 1; if (green < 0) green = 0; $ if (blue > 1) blue = 1; if (blue < 0) blue = 0; $ return txSetPixel (x, y, RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)), dc); } //----------------------------------------------------------------------------------------------------------------- inline COLORREF txGetPixel (double x, double y, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID; $ return txGDI ((Win32::GetPixel (dc, ROUND (x), ROUND (y))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txLine (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ bool ok = txGDI ((Win32::MoveToEx (dc, ROUND (x0), ROUND (y0), NULL)), dc); $ ok &= txGDI ((Win32::LineTo (dc, ROUND (x1), ROUND (y1) )), dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txRectangle (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::Rectangle (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txPolygon (const POINT points[], int numPoints, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (points)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI (!!(Win32::Polygon (dc, points, numPoints)), dc); } //----------------------------------------------------------------------------------------------------------------- bool txEllipse (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::Ellipse (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txCircle (double x, double y, double r) { $1 return txEllipse (x-r, y-r, x+r, y+r); } //----------------------------------------------------------------------------------------------------------------- bool txArc (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Arc (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txPie (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Pie (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txChord (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) }; $ double start = startAngle * txPI/180, end = (startAngle + totalAngle) * txPI/180; $ return txGDI (!!(Win32::Chord (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1), ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)), ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc); } //----------------------------------------------------------------------------------------------------------------- bool txFloodFill (double x, double y, COLORREF color /*= TX_TRANSPARENT*/, DWORD mode /*= FLOODFILLSURFACE*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ if (color == TX_TRANSPARENT) color = txGetPixel (x, y, dc); $ return txGDI (!!(Win32::ExtFloodFill (dc, ROUND (x), ROUND (y), color, mode)), dc); } //----------------------------------------------------------------------------------------------------------------- bool txTextOut (double x, double y, const char text[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (text)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ int len = (int) strlen (text); $ bool ok = txGDI (!!(Win32::TextOut (dc, ROUND (x), ROUND (y), text, len)), dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txDrawText (double x0, double y0, double x1, double y1, const char text[], unsigned format /*= DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS*/, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (text)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; #if !defined (NDEBUG) $ if (x0 > x1) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Параметр x0 = %lg больше, чем x1 = %lg. Текст выводиться не будет.", x0, x1); } $ if (y0 > y1) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Параметр y0 = %lg больше, чем y1 = %lg. Текст выводиться не будет.", y0, y1); } #endif $ RECT r = { ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1) }; $ if (!strchr (text, '\n')) format |= DT_SINGLELINE; $ unsigned prev = txSetTextAlign (TA_LEFT | TA_TOP | TA_NOUPDATECP, dc); $ bool ok = false; $ if (Win32::DrawText) { $ ok = !!txGDI ((Win32::DrawText (dc, text, -1, &r, format)), dc); $ Win32::GetPixel (dc, 0, 0); $ ok = true; } else { $ txTextOut ((x0 + x1) / 2, (y0 + y1) / 2, text); $ ok = false; } $ txSetTextAlign (prev, dc); $ return ok; } //----------------------------------------------------------------------------------------------------------------- HFONT txSelectFont (const char name[], double sizeY, double sizeX /*= -1*/, int bold /*= FW_DONTCARE*/, bool italic /*= false*/, bool underline /*= false*/, bool strikeout /*= false*/, double angle /*= 0*/, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (name)) return NULL; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL; $ HFONT font = txFontExist (name)? Win32::CreateFont (ROUND (sizeY), ROUND ((sizeX >= 0)? sizeX : sizeY/3), ROUND (angle*10), 0, bold, italic, underline, strikeout, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, name) : (HFONT) Win32::GetStockObject (SYSTEM_FIXED_FONT); $ _txBuffer_Select (font, dc); $ return font; } //----------------------------------------------------------------------------------------------------------------- SIZE txGetTextExtent (const char text[], HDC dc /*= txDC()*/) { $1 SIZE size = {-1, -1}; $ if (_TX_ARGUMENT_FAILED (text)) return size; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return size; $ size_t len = strlen (text); $ txGDI ((Win32::GetTextExtentPoint32 (dc, text, (int) len, &size)), dc) asserted; $ return size; } //----------------------------------------------------------------------------------------------------------------- int txGetTextExtentX (const char text[], HDC dc /*= txDC()*/) { $1 return txGetTextExtent (text, dc) .cx; } //----------------------------------------------------------------------------------------------------------------- int txGetTextExtentY (const char text[], HDC dc /*= txDC()*/) { $1 return txGetTextExtent (text, dc) .cy; } //----------------------------------------------------------------------------------------------------------------- unsigned txSetTextAlign (unsigned align /*= TA_CENTER | TA_BASELINE*/, HDC dc /*= txDC()*/) { $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return txGDI ((Win32::SetTextAlign (dc, align)), dc); } //----------------------------------------------------------------------------------------------------------------- LOGFONT* txFontExist (const char name[]) { $1 if (_TX_ARGUMENT_FAILED (name)) return NULL; $ static LOGFONT font = {}; $ font.lfCharSet = DEFAULT_CHARSET; $ strncpy_s (font.lfFaceName, sizeof (font.lfFaceName), name, sizeof (font.lfFaceName) - 1); $ struct tools { static int CALLBACK enumFonts (const LOGFONT* fnt, const TEXTMETRIC*, DWORD, LPARAM data) { $ if (_TX_ARGUMENT_FAILED (fnt)) return 0; $ if (_TX_ARGUMENT_FAILED (data)) return 0; #ifndef __STRICT_ANSI__ $ return _strnicmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE); #else $ return strncmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE); #endif } }; $ return txGDI ((Win32::EnumFontFamiliesEx (NULL, &font, tools::enumFonts, (LPARAM) &font, 0)), NULL) == 0? &font : NULL; } //----------------------------------------------------------------------------------------------------------------- bool txSelectObject (HGDIOBJ obj, HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (obj)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ return _txBuffer_Select (obj, dc); } //----------------------------------------------------------------------------------------------------------------- HDC txCreateCompatibleDC (double sizeX, double sizeY, HBITMAP bitmap /*= NULL*/) { $1 POINT size = { ROUND (sizeX), ROUND (sizeY) }; $ HDC dc = _txBuffer_Create (NULL, &size, bitmap); $ assert (dc); if (!dc) return NULL; $ txSetDefaults (dc); $ if (!_txCanvas_UserDCs) return dc; $ txAutoLock _lock; $ _txCanvas_UserDCs->push_back (dc); $ if (_txCanvas_UserDCs->size() >= _TX_BUFSIZE) {$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загрузили уже %d HDC, системе может стать плохо.", (int) _txCanvas_UserDCs->size()); } $ return dc; } //----------------------------------------------------------------------------------------------------------------- HDC txCreateDIBSection (double sizeX, double sizeY, RGBQUAD** pixels /*= NULL*/) { $1 RGBQUAD* buf = NULL; $ if (!pixels) pixels = &buf; $ BITMAPINFO info = {{ sizeof (info), ROUND (sizeX), ROUND (sizeY), 1, WORD (sizeof (RGBQUAD) * 8), BI_RGB }}; $ HDC dc = txCreateCompatibleDC (0, 0, Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (void**) pixels, NULL, 0)); $ RGBQUAD black = { 0, 0, 0, 255 }; $ for (int i = 0; i < sizeX * sizeY; i++) (*pixels)[i] = black; $ return dc; } //----------------------------------------------------------------------------------------------------------------- HDC txCreateDIBSection (double sizeX, double sizeY, COLORREF** pixels) { $1 return txCreateDIBSection (sizeX, sizeY, (RGBQUAD**) pixels); } //----------------------------------------------------------------------------------------------------------------- HDC txLoadImage (const char filename[], unsigned imageFlags /*= IMAGE_BITMAP*/, unsigned loadFlags /*= LR_LOADFROMFILE*/) { $1 if (_TX_ARGUMENT_FAILED (filename && *filename)) return NULL; $ HBITMAP image = (HBITMAP) Win32::LoadImage ((loadFlags & LR_LOADFROMFILE)? NULL : GetModuleHandle (NULL), filename, imageFlags, 0, 0, loadFlags); $ if (!image) return NULL; $ HDC dc = txCreateCompatibleDC (0, 0, image); $ if (!(loadFlags & LR_LOADFROMFILE)) return dc; $ static std::map <std::string, unsigned> loadTimes; $ std::string file = filename; $ unsigned time = GetTickCount(); $ if ((long) (time - loadTimes [file]) < _TX_TIMEOUT) {$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загружаете \"%s\" слишком часто, программа будет тормозить.", filename); } $ loadTimes [file] = time; $ return dc; } //----------------------------------------------------------------------------------------------------------------- bool txDeleteDC (HDC* pdc) { $1 if (_TX_ARGUMENT_FAILED (pdc)) return false; $ HDC dc = *pdc; $ bool ok = _txBuffer_Delete (pdc); $ if (!ok) return false; $ if (!_txCanvas_UserDCs) return ok; $ txAutoLock _lock; $ std::vector <HDC> ::iterator it = std::find (_txCanvas_UserDCs->begin(), _txCanvas_UserDCs->end(), dc); $ if (it != _txCanvas_UserDCs->end()) { std::swap (*it, _txCanvas_UserDCs->back()); _txCanvas_UserDCs->pop_back(); } $ return ok; } //----------------------------------------------------------------------------------------------------------------- bool txDeleteDC (HDC dc) { $1 return txDeleteDC (&dc); } //----------------------------------------------------------------------------------------------------------------- bool txBitBlt (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, unsigned operation /*= SRCCOPY*/) { $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; $ return txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), operation)), destImage); } //----------------------------------------------------------------------------------------------------------------- inline bool txBitBlt (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txBitBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource); } //----------------------------------------------------------------------------------------------------------------- bool txTransparentBlt (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, COLORREF transColor /*= TX_BLACK*/) { $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; #if !defined (NDEBUG) $ if (!(0 <= xSource && xSource + width <= size.x && 0 <= ySource && ySource + height <= size.y)) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, " "функция txTransparentBlt() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y); } #endif $ bool ok = (Win32::TransparentBlt != NULL); $ if (ok) { $ ok &= txGDI (!!(Win32::TransparentBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), transColor)), destImage); } else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); } return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txTransparentBlt (double xDest, double yDest, HDC sourceImage, COLORREF transColor /*= TX_BLACK*/, double xSource /*= 0*/, double ySource /*= 0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txTransparentBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, transColor); } //----------------------------------------------------------------------------------------------------------------- bool txAlphaBlend (HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { // Это проверки того, правильные ли HDC вы передали в функцию. // Не бойтесь долларов - <s>это не запрещенная валюта</s> это макросы для отладки TXLib'а. $1 if (_TX_HDC_FAILED (destImage)) return false; $ if (_TX_HDC_FAILED (sourceImage)) return false; // Это автоматическое определение размеров картинки (точнее, HDC источника - source) с помощью txGetExtent(). $ POINT size = txGetExtent (sourceImage); $ if (!width) width = size.x; $ if (!height) height = size.y; // Это проверка того, что картинка (или ее часть) правильно попадает в окно (точнее, HDC приемника - destination, dest). // Если она "вылезает" из окна в любую сторону, то Win32::AlphaBlend не будет работать. Эта проверка происходит только // в режиме отладки (когда не задан макрос NDEBUG - No Debugging, без отладки). #if !defined (NDEBUG) $ if (!(0 <= xSource && xSource + width <= size.x && 0 <= ySource && ySource + height <= size.y)) { $ SetLastError (ERROR_INVALID_DATA); $ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, " "функция txAlphaBlend() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y); } #endif // Это на случай, если параметр alpha вылезает за диапазон [0..1]. $ if (alpha < 0) alpha = 0; $ if (alpha > 1) alpha = 1; // Об этом см. ниже. $ BITMAP bmap = { 0, 0, 0, 0, 0, 24 }; $ bool ok = !!Win32::GetObject (txGDI ((Win32::GetCurrentObject (sourceImage, OBJ_BITMAP)), sourceImage), sizeof (bmap), &bmap); // // * Структура BLENDFUNCTION * // // Эта структура определяет, как цвета пикселей окна (точнее, source DC) и картинки смешиваются при рисовании с учетом // прозрачности. // // Параметры смешивания помещаются в группу из переменных - так называемую структуру. Переменные, входящие в структуру, // называются компонентами структуры. // // В этой структуре более всех важен ее третий компонент (см. ниже), задающий прозрачность картинки. // Если он равен нулю - то картинка будет считаться полностью прозрачной и вызов Win32::AlphaBlend ничего не нарисует. // Если он равен 255 - то картинка скопируется в окно полностью, без эффекта прозрачности. // // С помощью четвертого компонента структуры система рисования Windows (Win32 GDI) узнает, надо ли учитывать альфа-канал // в копируемой картинке или его надо игнорировать. Если в картинке фактически есть правильно построенный альфа-канал // (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу // AC_SRC_ALPHA. // // Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0. // // В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения // функции txAlphaBlend(). // // Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать // альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы // породите невнятный паленый код и безнадежно испортите себе карму. :(( // // На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо. // // ____1____ 2 ___________3___________ _______________________4________________________ // / \ | / \ / \. $ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) }; // // * Вызов стандартной функции Win32::AlphaBlend() * // // Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend() // вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите // "Windows AlphaBlend function" и почитайте про ее параметры. // // Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не // совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s> // это сделано для упрощения вызова функции txAlphaBlend(). $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<-- { // // vvvvv vvvvvv // <<-- $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<== sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<== destImage); // ^^^^^ ^^^^^^ // <<-- } // ||||| |||||| // <<-- // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<-- else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); $ ok = false; } // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки, MB_ICONEXCLAMATION);
11975 
11976  return n;
11977  }
11978 
11979 //-----------------------------------------------------------------------------------------------------------------
11980 
11981 intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...)
11982  {
11983  if (!format) return 0;
11984 
11985  va_list arg; va_start (arg, format);
11986  intptr_t ret = _tx_vsnprintf_s (stream, size, format, arg);
11987  va_end (arg);
11988 
11989  return ret;
11990  }
11991 
11992 //-----------------------------------------------------------------------------------------------------------------
11993 
11994 intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg)
11995  {
11996  if (!stream || !format) return 0;
11997 
11998  #if defined (_TRUNCATE)
11999  intptr_t ret = _vsnprintf_s (stream, size, _TRUNCATE, format, arg);
12000  #else
12001  intptr_t ret = _vsnprintf (stream, size, format, arg);
12002  #endif
12003 
12004  if (ret < 0 && size >= 4)
12005  {
12006  const char ellipsis[] = "...";
12007  size_t szEllipsis = sizeof (ellipsis) - 1;
12008 
12009  strncpy_s (stream + size - szEllipsis, szEllipsis+1, ellipsis, szEllipsis);
12010  }
12011 
12012  return (ret >= 0)? ret : size;
12013  }
12014 
12015 //-----------------------------------------------------------------------------------------------------------------
12016 
12017 #if defined (__CYGWIN__)
12018 
12019 int _getch()
12020  {
12021  termios oldattr = {}; tcgetattr (STDIN_FILENO, &oldattr);
12022 
12023  termios newattr = oldattr;
12024  newattr.c_lflag &= ~(ICANON | ECHO);
12025  tcsetattr (STDIN_FILENO, TCSANOW, &newattr);
12026 
12027  int ch = getchar();
12028 
12029  tcsetattr (STDIN_FILENO, TCSANOW, &oldattr);
12030 
12031  return ch;
12032  }
12033 
12034 //-----------------------------------------------------------------------------------------------------------------
12035 
12036 int _putch (int ch)
12037  {
12038  termios old = {}; tcgetattr (STDOUT_FILENO, &old);
12039 
12040  termios cur = old;
12041  cur.c_lflag &= ~ICANON;
12042  cur.c_lflag |= ECHO;
12043  tcsetattr (STDOUT_FILENO, TCSANOW, &cur);
12044 
12045  putchar (ch);
12046 
12047  tcsetattr (STDOUT_FILENO, TCSANOW, &old);
12048 
12049  return ch;
12050  }
12051 
12052 //-----------------------------------------------------------------------------------------------------------------
12053 
12054 int _kbhit()
12055  {
12056  termios old = {}; tcgetattr (STDIN_FILENO, &old);
12057 
12058  termios cur = old;
12059  cur.c_lflag &= ~(ICANON | ECHO);
12060  cur.c_cc[VMIN] = 1;
12061  cur.c_cc[VTIME] = 0;
12062 
12063  tcsetattr (STDIN_FILENO, TCSAFLUSH, &cur);
12064 
12065  fd_set fd = {}; FD_SET (STDIN_FILENO, &fd);
12066  timeval tv = {};
12067 
12068  int res = select (STDIN_FILENO + 1, &fd, NULL, NULL, &tv) && FD_ISSET (STDIN_FILENO, &fd);
12069 
12070  tcsetattr (STDIN_FILENO, TCSAFLUSH, &old);
12071 
12072  return res;
12073  }
12074 
12075 #endif
12076 
12077 //}
12078 //-----------------------------------------------------------------------------------------------------------------
12079 
12080 //-----------------------------------------------------------------------------------------------------------------
12081 //{ Information
12082 //-----------------------------------------------------------------------------------------------------------------
12083 
12084 const char* txGetModuleFileName (bool fileNameOnly /*= true*/)
12085  {
12086  static char name[MAX_PATH] = "";
12087 
12088  if (!*name)
12089  {
12090  if (!GetModuleFileName (NULL, name, sizeof (name) - 1)) *name = 0;
12091 
12092  char* ext = strrchr (name, '.');
12093  if (ext) _strlwr_s (ext, sizeof (name) - 1 - (ext - name));
12094  }
12095 
12096  assert (*name);
12097 
12098  if (fileNameOnly) return name;
12099 
12100  static char fullName[MAX_PATH] = "";
12101  strncpy_s (fullName, sizeof (fullName), name, sizeof (fullName) - 1);
12102 
12103  char* title = strrchr (fullName, '\\'); if (!title) title = fullName;
12104  char* ext = strrchr (fullName, '.'); if (!ext) ext = fullName + strlen (fullName);
12105 
12106  size_t sz = sizeof (fullName) - (ext - fullName);
12107  strncpy_s (ext, sz-1, " - TXLib", sz);
12108 
12109  return title + 1;
12110  }
12111 
12112 //-----------------------------------------------------------------------------------------------------------------
12113 
12114 const char* _txAppInfo()
12115  {
12116 $1 time_t timeT = time (NULL) - clock()/CLOCKS_PER_SEC;
12117  char timeS[32] = "";
12118  ctime_s (timeS, sizeof (timeS), &timeT);
12119 
12120  static char text[_TX_BUFSIZE] = "";
12121  char cwd [MAX_PATH] = "";
12122 
12123  _tx_snprintf_s (text, sizeof (text) - 1,
12124 
12125  "Developed with:\n\n"
12126  "The Dumb Artist Library (TX Library)\n"
12127  _TX_VERSION "\n" _TX_AUTHOR "\n"
12128  "See license on: http://txlib.ru\n\n"
12129 
12130  "TXLib file:" "\t" __FILE__ "\n"
12131  "Compiled:" "\t" __DATE__ " " __TIME__ ", " __TX_COMPILER__ ", %s, %d-bit, " _TX_BUILDMODE "\n"
12132  "Started:" "\t" "%.6s %.4s %.8s\n\n"
12133 
12134  "Run file:" "\t" "%s\n"
12135  "Directory:" "\t" "%s",
12136 
12137  #if defined (_MSC_VER)
12138  "MSVC Runtime",
12139  #elif defined (__CYGWIN__)
12140  "Cygwin Runtime",
12141  #elif defined (_GCC_VER) && defined (_WIN64)
12142  __mingw_get_crt_info(),
12143  #else
12144  "MinGW Runtime " TX_QUOTE (__MINGW32_MAJOR_VERSION) "." TX_QUOTE (__MINGW32_MINOR_VERSION),
12145  #endif
12146  (sizeof (void*) == sizeof (DWORD))? 32 : 64,
12147 
12148  timeS + 4, timeS + 20, timeS + 11, // These offsets are ANSI standardized
12150  _getcwd (cwd, sizeof (cwd) - 1));
12151 
12152  return text;
12153  }
12154 
12155 //}
12156 //-----------------------------------------------------------------------------------------------------------------
12157 
12159 //}
12160 //=================================================================================================================
12161 
12162 //=================================================================================================================
12163 //{ TXLib API implementation
12164 // Реализация TXLib API
12165 //=================================================================================================================
12166 
12167 inline const char* txVersion()
12168  {
12169  return _TX_VERSION;
12170  }
12171 
12172 //-----------------------------------------------------------------------------------------------------------------
12173 
12174 inline unsigned txVersionNumber()
12175  {
12176  return _TX_VER;
12177  }
12178 
12179 //-----------------------------------------------------------------------------------------------------------------
12180 
12181 inline HWND txWindow()
12182  {
12183 $0 return _txCanvas_Window;
12184  }
12185 
12186 //-----------------------------------------------------------------------------------------------------------------
12187 
12188 inline HDC& txDC()
12189  {
12190 $0 return _txCanvas_BackBuf[0];
12191  }
12192 
12193 //-----------------------------------------------------------------------------------------------------------------
12194 
12195 inline RGBQUAD* txVideoMemory()
12196  {
12197  return _txCanvas_Pixels;
12198  }
12199 
12200 //-----------------------------------------------------------------------------------------------------------------
12201 
12202 POINT txGetExtent (HDC dc /*= txDC()*/)
12203  {
12204 $0 static POINT err = {-1, -1};
12205 
12206  if (!dc) {$ POINT screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; return screen; };
12207 
12208  if (_TX_DEFAULT_HDC_FAILED (dc)) {$ return err; }
12209 
12210 $ BITMAP bmap = {};
12211 $ txGDI (Win32::GetObject (Win32::GetCurrentObject (dc, OBJ_BITMAP), sizeof (bmap), &bmap), dc) asserted;
12212 
12213 $ POINT size = { bmap.bmWidth, bmap.bmHeight };
12214 $ return size;
12215  }
12216 
12217 //-----------------------------------------------------------------------------------------------------------------
12218 
12219 int txGetExtentX (HDC dc /*= txDC()*/)
12220  {
12221  return txGetExtent (dc) .x;
12222  }
12223 
12224 //-----------------------------------------------------------------------------------------------------------------
12225 
12226 int txGetExtentY (HDC dc /*= txDC()*/)
12227  {
12228  return txGetExtent (dc) .y;
12229  }
12230 
12231 //-----------------------------------------------------------------------------------------------------------------
12232 
12233 bool txDestroyWindow (HWND wnd /*= txWindow()*/)
12234  {
12235 $1 if (!wnd || !txWindow()) return false;
12236 
12237 $ if (wnd != txWindow())
12238  {
12239 $ return !!SendNotifyMessage (txWindow(), _TX_WM_DESTROYWND, 0, (LPARAM) wnd);
12240  }
12241 
12242 $ if (SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0) == 0) return false;
12243 
12244 $ if (_txMain)
12245  {
12246 $ txNotifyIcon (NIIF_WARNING, NULL, "\n" "Очень, очень плохо завершать программу через txDestroyWindow().\n\n"
12247  "Возвращайтесь через main(), там вам будут рады.\n");
12248 $ Sleep (_TX_TIMEOUT);
12249  }
12250 
12251 $ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT);
12252 
12253 $ return _txCanvas_Window == NULL;
12254  }
12255 
12256 //-----------------------------------------------------------------------------------------------------------------
12257 
12258 HPEN txSetColor (COLORREF color, double thickness /*= 1*/, HDC dc /*= txDC()*/)
12259  {
12260 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
12261 
12262 $ HPEN pen = Win32::CreatePen ((color == TX_TRANSPARENT? PS_NULL : PS_SOLID), ROUND (thickness), color);
12263 
12264 $ if (!pen) return (HPEN) NULL;
12265 
12266 $ if (!_txBuffer_Select (pen, dc))
12267  {
12268 $ Win32::DeleteObject (pen);
12269 $ return (HPEN) NULL;
12270  }
12271 
12272 $ if (txGDI (Win32::SetTextColor (dc, color), dc) == CLR_INVALID)
12273  {$ return (HPEN) NULL; }
12274 
12275 $ return pen;
12276  }
12277 
12278 //-----------------------------------------------------------------------------------------------------------------
12279 
12280 COLORREF txColor (double red, double green, double blue)
12281  {
12282 $1 if (red > 1) red = 1; if (red < 0) red = 0;
12283 $ if (green > 1) green = 1; if (green < 0) green = 0;
12284 $ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
12285 
12286 $ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255));
12287 
12288 $ return txSetColor (color)? color : CLR_INVALID;
12289  }
12290 
12291 //-----------------------------------------------------------------------------------------------------------------
12292 
12293 COLORREF txGetColor (HDC dc /*= txDC()*/)
12294  {
12295 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
12296 
12297 $ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_PEN)), dc);
12298 $ assert (obj); if (!obj) return CLR_INVALID;
12299 
12300 $ union { EXTLOGPEN extLogPen; LOGPEN LogPen; } buf = {};
12301 
12302 $ int size = Win32::GetObject (obj, 0, NULL);
12303 $ Win32::GetObject (obj, sizeof (buf), &buf) asserted;
12304 
12305 $ return (size == sizeof (LOGPEN))? buf.LogPen.lopnColor : buf.extLogPen.elpColor;
12306  }
12307 
12308 //-----------------------------------------------------------------------------------------------------------------
12309 
12310 HBRUSH txSetFillColor (COLORREF color, HDC dc /*= txDC()*/)
12311  {
12312 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
12313 
12314 $ HBRUSH brush = (color == TX_TRANSPARENT)? (HBRUSH) Win32::GetStockObject (HOLLOW_BRUSH) : Win32::CreateSolidBrush (color);
12315 
12316 $ return (_txBuffer_Select (brush, dc))? brush : (Win32::DeleteObject (brush), (HBRUSH) NULL);
12317  }
12318 
12319 //-----------------------------------------------------------------------------------------------------------------
12320 
12321 COLORREF txFillColor (double red, double green, double blue)
12322  {
12323 $1 if (red > 1) red = 1; if (red < 0) red = 0;
12324 $ if (green > 1) green = 1; if (green < 0) green = 0;
12325 $ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
12326 
12327 $ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255));
12328 
12329 $ return txSetFillColor (color)? color : CLR_INVALID;
12330  }
12331 
12332 //-----------------------------------------------------------------------------------------------------------------
12333 
12334 COLORREF txGetFillColor (HDC dc /*= txDC()*/)
12335  {
12336 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
12337 
12338 $ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_BRUSH)), dc);
12339 $ assert (obj); if (!obj) return CLR_INVALID;
12340 
12341 $ LOGBRUSH buf = {};
12342 $ txGDI ((Win32::GetObject (obj, sizeof (buf), &buf)), dc) asserted;
12343 
12344 $ return buf.lbColor;
12345  }
12346 
12347 //-----------------------------------------------------------------------------------------------------------------
12348 
12349 bool txClear (HDC dc /*= txDC()*/)
12350  {
12351 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12352 
12353 $ POINT size = txGetExtent (dc);
12354 $ return txGDI (!!(Win32::PatBlt (dc, 0, 0, size.x, size.y, PATCOPY)), dc);
12355  }
12356 
12357 //-----------------------------------------------------------------------------------------------------------------
12358 
12359 inline bool txSetPixel (double x, double y, COLORREF color, HDC dc /*= txDC()*/)
12360  {
12361 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12362 
12363 $ txGDI ((Win32::SetPixel (dc, ROUND (x), ROUND (y), color)), dc);
12364 
12365 $ return true;
12366  }
12367 
12368 //-----------------------------------------------------------------------------------------------------------------
12369 
12370 inline bool txPixel (double x, double y, double red, double green, double blue, HDC dc /*= txDC()*/)
12371  {
12372 $1 if (red > 1) red = 1; if (red < 0) red = 0;
12373 $ if (green > 1) green = 1; if (green < 0) green = 0;
12374 $ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
12375 
12376 $ return txSetPixel (x, y, RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)), dc);
12377  }
12378 
12379 //-----------------------------------------------------------------------------------------------------------------
12380 
12381 inline COLORREF txGetPixel (double x, double y, HDC dc /*= txDC()*/)
12382  {
12383 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
12384 
12385 $ return txGDI ((Win32::GetPixel (dc, ROUND (x), ROUND (y))), dc);
12386  }
12387 
12388 //-----------------------------------------------------------------------------------------------------------------
12389 
12390 bool txLine (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
12391  {
12392 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12393 
12394 $ bool ok = txGDI ((Win32::MoveToEx (dc, ROUND (x0), ROUND (y0), NULL)), dc);
12395 $ ok &= txGDI ((Win32::LineTo (dc, ROUND (x1), ROUND (y1) )), dc);
12396 
12397 $ return ok;
12398  }
12399 
12400 //-----------------------------------------------------------------------------------------------------------------
12401 
12402 bool txRectangle (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
12403  {
12404 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12405 
12406 $ return txGDI ((Win32::Rectangle (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc);
12407  }
12408 
12409 //-----------------------------------------------------------------------------------------------------------------
12410 
12411 bool txPolygon (const POINT points[], int numPoints, HDC dc /*= txDC()*/)
12412  {
12413 $1 if (_TX_ARGUMENT_FAILED (points)) return false;
12414 $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12415 
12416 $ return txGDI (!!(Win32::Polygon (dc, points, numPoints)), dc);
12417  }
12418 
12419 //-----------------------------------------------------------------------------------------------------------------
12420 
12421 bool txEllipse (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
12422  {
12423 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12424 
12425 $ return txGDI ((Win32::Ellipse (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc);
12426  }
12427 
12428 //-----------------------------------------------------------------------------------------------------------------
12429 
12430 bool txCircle (double x, double y, double r)
12431  {
12432 $1 return txEllipse (x-r, y-r, x+r, y+r);
12433  }
12434 
12435 //-----------------------------------------------------------------------------------------------------------------
12436 
12437 bool txArc (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
12438  {
12439 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12440 
12441 $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
12442 
12443 $ double start = startAngle * txPI/180,
12444  end = (startAngle + totalAngle) * txPI/180;
12445 
12446 $ return txGDI (!!(Win32::Arc (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
12447  ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
12448  ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
12449  }
12450 
12451 //-----------------------------------------------------------------------------------------------------------------
12452 
12453 bool txPie (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
12454  {
12455 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12456 
12457 $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
12458 
12459 $ double start = startAngle * txPI/180,
12460  end = (startAngle + totalAngle) * txPI/180;
12461 
12462 $ return txGDI (!!(Win32::Pie (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
12463  ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
12464  ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
12465  }
12466 
12467 //-----------------------------------------------------------------------------------------------------------------
12468 
12469 bool txChord (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
12470  {
12471 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12472 
12473 $ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
12474 
12475 $ double start = startAngle * txPI/180,
12476  end = (startAngle + totalAngle) * txPI/180;
12477 
12478 $ return txGDI (!!(Win32::Chord (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
12479  ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
12480  ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
12481  }
12482 
12483 //-----------------------------------------------------------------------------------------------------------------
12484 
12485 bool txFloodFill (double x, double y,
12486  COLORREF color /*= TX_TRANSPARENT*/, DWORD mode /*= FLOODFILLSURFACE*/, HDC dc /*= txDC()*/)
12487  {
12488 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12489 
12490 $ if (color == TX_TRANSPARENT) color = txGetPixel (x, y, dc);
12491 
12492 $ return txGDI (!!(Win32::ExtFloodFill (dc, ROUND (x), ROUND (y), color, mode)), dc);
12493  }
12494 
12495 //-----------------------------------------------------------------------------------------------------------------
12496 
12497 bool txTextOut (double x, double y, const char text[], HDC dc /*= txDC()*/)
12498  {
12499 $1 if (_TX_ARGUMENT_FAILED (text)) return false;
12500 $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12501 
12502 $ int len = (int) strlen (text);
12503 $ bool ok = txGDI (!!(Win32::TextOut (dc, ROUND (x), ROUND (y), text, len)), dc);
12504 
12505 $ return ok;
12506  }
12507 
12508 //-----------------------------------------------------------------------------------------------------------------
12509 
12510 bool txDrawText (double x0, double y0, double x1, double y1, const char text[],
12511  unsigned format /*= DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS*/,
12512  HDC dc /*= txDC()*/)
12513  {
12514 $1 if (_TX_ARGUMENT_FAILED (text)) return false;
12515 $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12516 
12517 #if !defined (NDEBUG)
12518 
12519 $ if (x0 > x1)
12520  {
12521 $ SetLastError (ERROR_INVALID_DATA);
12522 $ TX_ERROR ("Параметр x0 = %lg больше, чем x1 = %lg. Текст выводиться не будет.", x0, x1);
12523  }
12524 
12525 $ if (y0 > y1)
12526  {
12527 $ SetLastError (ERROR_INVALID_DATA);
12528 $ TX_ERROR ("Параметр y0 = %lg больше, чем y1 = %lg. Текст выводиться не будет.", y0, y1);
12529  }
12530 
12531 #endif
12532 
12533 $ RECT r = { ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1) };
12534 
12535 $ if (!strchr (text, '\n')) format |= DT_SINGLELINE;
12536 
12537 $ unsigned prev = txSetTextAlign (TA_LEFT | TA_TOP | TA_NOUPDATECP, dc);
12538 
12539 $ bool ok = false;
12540 
12541 $ if (Win32::DrawText)
12542  {
12543 $ ok = !!txGDI ((Win32::DrawText (dc, text, -1, &r, format)), dc);
12544 $ Win32::GetPixel (dc, 0, 0);
12545 $ ok = true;
12546  }
12547  else
12548  {
12549 $ txTextOut ((x0 + x1) / 2, (y0 + y1) / 2, text);
12550 $ ok = false;
12551  }
12552 
12553 $ txSetTextAlign (prev, dc);
12554 
12555 $ return ok;
12556  }
12557 
12558 //-----------------------------------------------------------------------------------------------------------------
12559 
12560 HFONT txSelectFont (const char name[], double sizeY, double sizeX /*= -1*/,
12561  int bold /*= FW_DONTCARE*/, bool italic /*= false*/, bool underline /*= false*/,
12562  bool strikeout /*= false*/, double angle /*= 0*/,
12563  HDC dc /*= txDC()*/)
12564  {
12565 $1 if (_TX_ARGUMENT_FAILED (name)) return NULL;
12566 $ if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
12567 
12568 $ HFONT font = txFontExist (name)?
12569  Win32::CreateFont (ROUND (sizeY), ROUND ((sizeX >= 0)? sizeX : sizeY/3),
12570  ROUND (angle*10), 0, bold, italic, underline, strikeout,
12571  RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
12572  DEFAULT_QUALITY, DEFAULT_PITCH, name)
12573  :
12574  (HFONT) Win32::GetStockObject (SYSTEM_FIXED_FONT);
12575 
12576 $ _txBuffer_Select (font, dc);
12577 
12578 $ return font;
12579  }
12580 
12581 //-----------------------------------------------------------------------------------------------------------------
12582 
12583 SIZE txGetTextExtent (const char text[], HDC dc /*= txDC()*/)
12584  {
12585 $1 SIZE size = {-1, -1};
12586 
12587 $ if (_TX_ARGUMENT_FAILED (text)) return size;
12588 $ if (_TX_DEFAULT_HDC_FAILED (dc)) return size;
12589 
12590 $ size_t len = strlen (text);
12591 $ txGDI ((Win32::GetTextExtentPoint32 (dc, text, (int) len, &size)), dc) asserted;
12592 
12593 $ return size;
12594  }
12595 
12596 //-----------------------------------------------------------------------------------------------------------------
12597 
12598 int txGetTextExtentX (const char text[], HDC dc /*= txDC()*/)
12599  {
12600 $1 return txGetTextExtent (text, dc) .cx;
12601  }
12602 
12603 //-----------------------------------------------------------------------------------------------------------------
12604 
12605 int txGetTextExtentY (const char text[], HDC dc /*= txDC()*/)
12606  {
12607 $1 return txGetTextExtent (text, dc) .cy;
12608  }
12609 
12610 //-----------------------------------------------------------------------------------------------------------------
12611 
12612 unsigned txSetTextAlign (unsigned align /*= TA_CENTER | TA_BASELINE*/, HDC dc /*= txDC()*/)
12613  {
12614 $1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12615 
12616 $ return txGDI ((Win32::SetTextAlign (dc, align)), dc);
12617  }
12618 
12619 //-----------------------------------------------------------------------------------------------------------------
12620 
12621 LOGFONT* txFontExist (const char name[])
12622  {
12623 $1 if (_TX_ARGUMENT_FAILED (name)) return NULL;
12624 
12625 $ static LOGFONT font = {};
12626 $ font.lfCharSet = DEFAULT_CHARSET;
12627 $ strncpy_s (font.lfFaceName, sizeof (font.lfFaceName), name, sizeof (font.lfFaceName) - 1);
12628 
12629 $ struct tools
12630  {
12631  static int CALLBACK enumFonts (const LOGFONT* fnt, const TEXTMETRIC*, DWORD, LPARAM data)
12632  {
12633 $ if (_TX_ARGUMENT_FAILED (fnt)) return 0;
12634 $ if (_TX_ARGUMENT_FAILED (data)) return 0;
12635 
12636  #ifndef __STRICT_ANSI__
12637 $ return _strnicmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
12638 
12639  #else
12640 $ return strncmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
12641 
12642  #endif
12643  }
12644  };
12645 
12646 $ return txGDI ((Win32::EnumFontFamiliesEx (NULL, &font, tools::enumFonts, (LPARAM) &font, 0)), NULL) == 0? &font : NULL;
12647  }
12648 
12649 //-----------------------------------------------------------------------------------------------------------------
12650 
12651 bool txSelectObject (HGDIOBJ obj, HDC dc /*= txDC()*/)
12652  {
12653 $1 if (_TX_ARGUMENT_FAILED (obj)) return false;
12654 $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
12655 
12656 $ return _txBuffer_Select (obj, dc);
12657  }
12658 
12659 //-----------------------------------------------------------------------------------------------------------------
12660 
12661 HDC txCreateCompatibleDC (double sizeX, double sizeY, HBITMAP bitmap /*= NULL*/)
12662  {
12663 $1 POINT size = { ROUND (sizeX), ROUND (sizeY) };
12664 
12665 $ HDC dc = _txBuffer_Create (NULL, &size, bitmap);
12666 $ assert (dc); if (!dc) return NULL;
12667 
12668 $ txSetDefaults (dc);
12669 
12670 $ if (!_txCanvas_UserDCs) return dc;
12671 
12672 $ txAutoLock _lock;
12673 $ _txCanvas_UserDCs->push_back (dc);
12674 
12675 $ if (_txCanvas_UserDCs->size() >= _TX_BUFSIZE)
12676  {$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загрузили уже %d HDC, системе может стать плохо.", (int) _txCanvas_UserDCs->size()); }
12677 
12678 $ return dc;
12679  }
12680 
12681 //-----------------------------------------------------------------------------------------------------------------
12682 
12683 HDC txCreateDIBSection (double sizeX, double sizeY, RGBQUAD** pixels /*= NULL*/)
12684  {
12685 $1 RGBQUAD* buf = NULL;
12686 $ if (!pixels) pixels = &buf;
12687 
12688 $ BITMAPINFO info = {{ sizeof (info), ROUND (sizeX), ROUND (sizeY), 1, WORD (sizeof (RGBQUAD) * 8), BI_RGB }};
12689 
12690 $ HDC dc = txCreateCompatibleDC (0, 0, Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (void**) pixels, NULL, 0));
12691 
12692 $ RGBQUAD black = { 0, 0, 0, 255 };
12693 $ for (int i = 0; i < sizeX * sizeY; i++) (*pixels)[i] = black;
12694 
12695 $ return dc;
12696  }
12697 
12698 //-----------------------------------------------------------------------------------------------------------------
12699 
12700 HDC txCreateDIBSection (double sizeX, double sizeY, COLORREF** pixels)
12701  {
12702 $1 return txCreateDIBSection (sizeX, sizeY, (RGBQUAD**) pixels);
12703  }
12704 
12705 //-----------------------------------------------------------------------------------------------------------------
12706 
12707 HDC txLoadImage (const char filename[], unsigned imageFlags /*= IMAGE_BITMAP*/, unsigned loadFlags /*= LR_LOADFROMFILE*/)
12708  {
12709 $1 if (_TX_ARGUMENT_FAILED (filename && *filename)) return NULL;
12710 
12711 $ HBITMAP image = (HBITMAP) Win32::LoadImage ((loadFlags & LR_LOADFROMFILE)? NULL : GetModuleHandle (NULL),
12712  filename, imageFlags, 0, 0, loadFlags);
12713 $ if (!image) return NULL;
12714 
12715 $ HDC dc = txCreateCompatibleDC (0, 0, image);
12716 
12717 $ if (!(loadFlags & LR_LOADFROMFILE)) return dc;
12718 
12719 $ static std::map <std::string, unsigned> loadTimes;
12720 $ std::string file = filename;
12721 $ unsigned time = GetTickCount();
12722 
12723 $ if ((long) (time - loadTimes [file]) < _TX_TIMEOUT)
12724  {$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загружаете \"%s\" слишком часто, программа будет тормозить.", filename); }
12725 
12726 $ loadTimes [file] = time;
12727 
12728 $ return dc;
12729  }
12730 
12731 //-----------------------------------------------------------------------------------------------------------------
12732 
12733 bool txDeleteDC (HDC* pdc)
12734  {
12735 $1 if (_TX_ARGUMENT_FAILED (pdc)) return false;
12736 
12737 $ HDC dc = *pdc;
12738 $ bool ok = _txBuffer_Delete (pdc);
12739 $ if (!ok) return false;
12740 
12741 $ if (!_txCanvas_UserDCs) return ok;
12742 
12743 $ txAutoLock _lock;
12744 $ std::vector <HDC> ::iterator it = std::find (_txCanvas_UserDCs->begin(), _txCanvas_UserDCs->end(), dc);
12745 $ if (it != _txCanvas_UserDCs->end()) { std::swap (*it, _txCanvas_UserDCs->back()); _txCanvas_UserDCs->pop_back(); }
12746 
12747 $ return ok;
12748  }
12749 
12750 //-----------------------------------------------------------------------------------------------------------------
12751 
12752 bool txDeleteDC (HDC dc)
12753  {
12754 $1 return txDeleteDC (&dc);
12755  }
12756 
12757 //-----------------------------------------------------------------------------------------------------------------
12758 
12759 bool txBitBlt (HDC destImage, double xDest, double yDest, double width, double height,
12760  HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, unsigned operation /*= SRCCOPY*/)
12761  {
12762 $1 if (_TX_HDC_FAILED (destImage)) return false;
12763 $ if (_TX_HDC_FAILED (sourceImage)) return false;
12764 
12765 $ POINT size = txGetExtent (sourceImage);
12766 $ if (!width) width = size.x;
12767 $ if (!height) height = size.y;
12768 
12769 $ return txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
12770  sourceImage, ROUND (xSource), ROUND (ySource), operation)), destImage);
12771  }
12772 
12773 //-----------------------------------------------------------------------------------------------------------------
12774 
12775 inline bool txBitBlt (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/)
12776  {
12777 $1 if (_TX_TXWINDOW_FAILED()) return false;
12778 
12779 $ return txBitBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource);
12780  }
12781 
12782 //-----------------------------------------------------------------------------------------------------------------
12783 
12784 bool txTransparentBlt (HDC destImage, double xDest, double yDest, double width, double height,
12785  HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, COLORREF transColor /*= TX_BLACK*/)
12786  {
12787 $1 if (_TX_HDC_FAILED (destImage)) return false;
12788 $ if (_TX_HDC_FAILED (sourceImage)) return false;
12789 
12790 $ POINT size = txGetExtent (sourceImage);
12791 $ if (!width) width = size.x;
12792 $ if (!height) height = size.y;
12793 
12794 #if !defined (NDEBUG)
12795 
12796 $ if (!(0 <= xSource && xSource + width <= size.x &&
12797  0 <= ySource && ySource + height <= size.y))
12798  {
12799 $ SetLastError (ERROR_INVALID_DATA);
12800 $ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
12801  "функция txTransparentBlt() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y);
12802  }
12803 
12804 #endif
12805 
12806 $ bool ok = (Win32::TransparentBlt != NULL);
12807 
12808 $ if (ok)
12809  {
12810 $ ok &= txGDI (!!(Win32::TransparentBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
12811  sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), transColor)),
12812  destImage);
12813  }
12814  else
12815  {
12816 $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
12817  sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)),
12818  destImage);
12819  }
12820 
12821  return ok;
12822  }
12823 
12824 //-----------------------------------------------------------------------------------------------------------------
12825 
12826 inline bool txTransparentBlt (double xDest, double yDest, HDC sourceImage,
12827  COLORREF transColor /*= TX_BLACK*/, double xSource /*= 0*/, double ySource /*= 0*/)
12828  {
12829 $1 if (_TX_TXWINDOW_FAILED()) return false;
12830 
12831 $ return txTransparentBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, transColor);
12832  }
12833 
12834 //-----------------------------------------------------------------------------------------------------------------
12835 
12836 bool txAlphaBlend (HDC destImage, double xDest, double yDest, double width, double height,
12837  HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/)
12838  {
12839  // Это проверки того, правильные ли HDC вы передали в функцию.
12840  // Не бойтесь долларов - <s>это не запрещенная валюта</s> это макросы для отладки TXLib'а.
12841 
12842 $1 if (_TX_HDC_FAILED (destImage)) return false;
12843 $ if (_TX_HDC_FAILED (sourceImage)) return false;
12844 
12845  // Это автоматическое определение размеров картинки (точнее, HDC источника - source) с помощью txGetExtent().
12846 
12847 $ POINT size = txGetExtent (sourceImage);
12848 $ if (!width) width = size.x;
12849 $ if (!height) height = size.y;
12850 
12851  // Это проверка того, что картинка (или ее часть) правильно попадает в окно (точнее, HDC приемника - destination, dest).
12852  // Если она "вылезает" из окна в любую сторону, то Win32::AlphaBlend не будет работать. Эта проверка происходит только
12853  // в режиме отладки (когда не задан макрос NDEBUG - No Debugging, без отладки).
12854 
12855 #if !defined (NDEBUG)
12856 
12857 $ if (!(0 <= xSource && xSource + width <= size.x &&
12858  0 <= ySource && ySource + height <= size.y))
12859  {
12860 $ SetLastError (ERROR_INVALID_DATA);
12861 $ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
12862  "функция txAlphaBlend() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y);
12863  }
12864 
12865 #endif
12866 
12867  // Это на случай, если параметр alpha вылезает за диапазон [0..1].
12868 
12869 $ if (alpha < 0) alpha = 0;
12870 $ if (alpha > 1) alpha = 1;
12871 
12872  // Об этом см. ниже.
12873 
12874 $ BITMAP bmap = { 0, 0, 0, 0, 0, 24 };
12875 $ bool ok = !!Win32::GetObject (txGDI ((Win32::GetCurrentObject (sourceImage, OBJ_BITMAP)), sourceImage), sizeof (bmap), &bmap);
12876 
12877  //
12878  // * Структура BLENDFUNCTION *
12879  //
12880  // Эта структура определяет, как цвета пикселей окна (точнее, source DC) и картинки смешиваются при рисовании с учетом
12881  // прозрачности.
12882  //
12883  // Параметры смешивания помещаются в группу из переменных - так называемую структуру. Переменные, входящие в структуру,
12884  // называются компонентами структуры.
12885  //
12886  // В этой структуре более всех важен ее третий компонент (см. ниже), задающий прозрачность картинки.
12887  // Если он равен нулю - то картинка будет считаться полностью прозрачной и вызов Win32::AlphaBlend ничего не нарисует.
12888  // Если он равен 255 - то картинка скопируется в окно полностью, без эффекта прозрачности.
12889  //
12890  // С помощью четвертого компонента структуры система рисования Windows (Win32 GDI) узнает, надо ли учитывать альфа-канал // в копируемой картинке или его надо игнорировать. Если в картинке фактически есть правильно построенный альфа-канал // (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу // AC_SRC_ALPHA. // // Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0. // // В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения // функции txAlphaBlend(). // // Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать // альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы // породите невнятный паленый код и безнадежно испортите себе карму. :(( // // На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо. // // ____1____ 2 ___________3___________ _______________________4________________________ // / \ | / \ / \. $ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) }; // // * Вызов стандартной функции Win32::AlphaBlend() * // // Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend() // вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите // "Windows AlphaBlend function" и почитайте про ее параметры. // // Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не // совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s> // это сделано для упрощения вызова функции txAlphaBlend(). $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<-- { // // vvvvv vvvvvv // <<-- $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<== sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<== destImage); // ^^^^^ ^^^^^^ // <<-- } // ||||| |||||| // <<-- // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<-- else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); $ ok = false; } // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки
12891  // в копируемой картинке или его надо игнорировать. Если в картинке фактически есть правильно построенный альфа-канал // (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу // AC_SRC_ALPHA. // // Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0. // // В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения // функции txAlphaBlend(). // // Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать // альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы // породите невнятный паленый код и безнадежно испортите себе карму. :(( // // На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо. // // ____1____ 2 ___________3___________ _______________________4________________________ // / \ | / \ / \. $ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) }; // // * Вызов стандартной функции Win32::AlphaBlend() * // // Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend() // вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите // "Windows AlphaBlend function" и почитайте про ее параметры. // // Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не // совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s> // это сделано для упрощения вызова функции txAlphaBlend(). $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<-- { // // vvvvv vvvvvv // <<-- $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<== sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<== destImage); // ^^^^^ ^^^^^^ // <<-- } // ||||| |||||| // <<-- // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<-- else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); $ ok = false; } // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки
12892  // (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу // AC_SRC_ALPHA. // // Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0. // // В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения // функции txAlphaBlend(). // // Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать // альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы // породите невнятный паленый код и безнадежно испортите себе карму. :(( // // На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо. // // ____1____ 2 ___________3___________ _______________________4________________________ // / \ | / \ / \. $ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) }; // // * Вызов стандартной функции Win32::AlphaBlend() * // // Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend() // вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите // "Windows AlphaBlend function" и почитайте про ее параметры. // // Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не // совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s> // это сделано для упрощения вызова функции txAlphaBlend(). $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<-- { // // vvvvv vvvvvv // <<-- $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<== sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<== destImage); // ^^^^^ ^^^^^^ // <<-- } // ||||| |||||| // <<-- // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<-- else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); $ ok = false; } // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки
12893  // AC_SRC_ALPHA.
12894  //
12895  // Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0.
12896  //
12897  // В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения
12898  // функции txAlphaBlend().
12899  //
12900  // Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать
12901  // альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы
12902  // породите невнятный паленый код и безнадежно испортите себе карму. :((
12903  //
12904  // На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо.
12905  //
12906  // ____1____ 2 ___________3___________ _______________________4________________________
12907  // / \ | / \ / \.
12908 
12909 $ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) };
12910 
12911  //
12912  // * Вызов стандартной функции Win32::AlphaBlend() *
12913  //
12914  // Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend()
12915  // вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите // "Windows AlphaBlend function" и почитайте про ее параметры. // // Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не // совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s> // это сделано для упрощения вызова функции txAlphaBlend(). $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<-- { // // vvvvv vvvvvv // <<-- $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<== sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<== destImage); // ^^^^^ ^^^^^^ // <<-- } // ||||| |||||| // <<-- // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<-- else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); $ ok = false; } // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки
12916  // "Windows AlphaBlend function" и почитайте про ее параметры.
12917  //
12918  // Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не
12919  // совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s> // это сделано для упрощения вызова функции txAlphaBlend(). $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<-- { // // vvvvv vvvvvv // <<-- $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<== sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<== destImage); // ^^^^^ ^^^^^^ // <<-- } // ||||| |||||| // <<-- // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<-- else { $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)), destImage); $ ok = false; } // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки
12920  // эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s>
12921  // это сделано для упрощения вызова функции txAlphaBlend().
12922 
12923 $ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<--
12924  { // // vvvvv vvvvvv // <<--
12925 $ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<==
12926  sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<==
12927  destImage); // ^^^^^ ^^^^^^ // <<--
12928  } // ||||| |||||| // <<--
12929  // См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<--
12930 
12931  else
12932  {
12933 $ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
12934  sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)),
12935  destImage);
12936 $ ok = false;
12937  }
12938 
12939  // В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою
12940  // аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто. // Хотя это и сложнее.</s> // Но помни про паленый копипаст и карму, см. выше. Я предупредил. $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/) { $1 if (_TX_TXWINDOW_FAILED()) return false; $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha); } //----------------------------------------------------------------------------------------------------------------- HDC txUseAlpha (HDC image) { $1 if (_TX_HDC_FAILED (image)) return NULL; $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP); $ if (!bitmap) return NULL; $ DIBSECTION dib = {}; $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted; $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight }; $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }}; $ RGBQUAD* buf = NULL; $ bool isDIB = (dib.dsBm.bmPlanes == 1 && dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 && dib.dsBmih.biCompression == DIB_RGB_COLORS && dib.dsBm.bmBits); $ if (!isDIB) { $ buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ if (!buf) return NULL; $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; } else { $ buf = (RGBQUAD*) dib.dsBm.bmBits; } $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) { RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0); color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0); color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0); } $ if (!isDIB) { $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted; $ delete[] buf; } $ return image; } //----------------------------------------------------------------------------------------------------------------- bool txSaveImage (const char filename[], HDC dc /*= txDC()*/) { $1 if (_TX_ARGUMENT_FAILED (filename)) return false; $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false; $ POINT size = txGetExtent (dc); $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), szImg = (size.x * size.y) * sizeof (RGBQUAD); $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs }; $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }; $ bool ok = true; $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y]; $ ok &= (buf != NULL); $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y, buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted; $ FILE* f = NULL; $ if (ok) fopen_s (&f, filename, "wb"); $ ok &= (f != NULL); $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1); $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1); $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1); $ ok &= (f && fclose (f) == 0); $ delete[] buf; $ buf = NULL; $ return ok; } //----------------------------------------------------------------------------------------------------------------- inline void txRedrawWindow() { $1 txSleep (0); } //----------------------------------------------------------------------------------------------------------------- inline int txUpdateWindow (int update /*= true*/) { $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update); } //----------------------------------------------------------------------------------------------------------------- inline int txBegin() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- inline int txEnd() { $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1); $ return _txCanvas_RefreshLock; } //----------------------------------------------------------------------------------------------------------------- double txSleep (double time) { $1 LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ int lock = _txCanvas_RefreshLock; $ _txCanvas_RefreshLock = 0; $ HWND wnd = txWindow(); if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); } $ Sleep (ROUND ((time >= 0)? time : 0)); $ _txCanvas_RefreshLock = lock; $ LARGE_INTEGER stop = {}; $ QueryPerformanceCounter (&stop) asserted; $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart; } //----------------------------------------------------------------------------------------------------------------- bool txLock (bool wait /*= true*/) { $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; } else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); } } //----------------------------------------------------------------------------------------------------------------- bool txUnlock() { $0 LeaveCriticalSection (&_txCanvas_LockBackBuf); $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0); $ return false; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T txUnlock (T value) { $1 txUnlock(); $ return value; } //----------------------------------------------------------------------------------------------------------------- inline POINT txMousePos() { $1 POINT pos = {}; $ GetCursorPos (&pos); $ if (txWindow()) {$ ScreenToClient (txWindow(), &pos); } $ return pos; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseX() { return txMousePos() .x; } //----------------------------------------------------------------------------------------------------------------- inline int txMouseY() { return txMousePos() .y; } //----------------------------------------------------------------------------------------------------------------- inline unsigned txMouseButtons() { $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2 } //----------------------------------------------------------------------------------------------------------------- unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/) { unsigned oldAttr = txGetConsoleAttr(); SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color); return oldAttr; } //----------------------------------------------------------------------------------------------------------------- unsigned txGetConsoleAttr() { CONSOLE_SCREEN_BUFFER_INFO con = {}; GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con); return con.wAttributes; } //----------------------------------------------------------------------------------------------------------------- POINT txSetConsoleCursorPos (double x, double y) { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left), (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) }; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted; $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return prev; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleCursorPos() { $1 POINT fontSz = txGetConsoleFontSize(); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x), ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) }; $ return pos; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleExtent() { $1 CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted; $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1, con.srWindow.Bottom - con.srWindow.Top + 1 }; $ return size; } //----------------------------------------------------------------------------------------------------------------- bool txClearConsole() { $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); $ CONSOLE_SCREEN_BUFFER_INFO con = {}; $ GetConsoleScreenBufferInfo (out, &con) asserted; $ COORD start = {con.srWindow.Left, con.srWindow.Top}; $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) * (con.srWindow.Bottom - con.srWindow.Top + 1); $ DWORD written = 0; $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted; $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted; $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted; $ return written == len; } //----------------------------------------------------------------------------------------------------------------- POINT txGetConsoleFontSize() { $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}}; $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font)); $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y }; $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC()); $ POINT sizeFont = { size.cx, size.cy }; $ return sizeFont; } //----------------------------------------------------------------------------------------------------------------- bool txTextCursor (bool blink /*= true*/) { $1 bool old = _txConsole_IsBlinking; $ _txConsole_IsBlinking = blink; $ return old; } //----------------------------------------------------------------------------------------------------------------- bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/) { $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT; $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC; $ if (!filename) mode = SND_PURGE; $ return !!Win32::PlaySound (filename, NULL, mode); } //----------------------------------------------------------------------------------------------------------------- int txSpeak (const char* text, ...) { $1 bool verbose = false; (void) verbose; $ bool async = false; (void) async; $ for (; text && *text; text++) { if (*text == '\a') {$ async = true; } else if (*text == '\v') {$ verbose = true; } else break; } $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!"; $ va_list arg; va_start (arg, text); if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); } $ va_end (arg); #ifdef TX_USE_SPEAK if (text && verbose) {$ printf ("%s", textA); } $ int time = GetTickCount(); $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L""; $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW)); $ static ISpVoice* voice = NULL; $ if (text && !voice) { $ HRESULT res = Win32::CoInitialize (NULL); if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); } } $ if (text && voice) { $ Win32::_fpreset(); $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL); $ tx_fpreset(); } $ if (!text && voice) { $ voice->Release(); $ voice = NULL; $ Win32::CoUninitialize(); } $ return (voice)? GetTickCount() - time : -1; #else $ if (text) { $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK); $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA); $ txSetConsoleAttr (oldAttr); } $ return -1; #endif } //----------------------------------------------------------------------------------------------------------------- intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[], double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/) { $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1; $ int time = GetTickCount(); $ static char processUID [64] = ""; if (!*processUID) { $ FILETIME startTime = {}, null = {}; $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted; $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo", (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted; } $ if (!fileName) { $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern $ return 0; } $ static const char* vlcPath = _txPlayVideo_FindVLC(); $ if (!vlcPath || _access (vlcPath, 0) != 0) { $ static int once = false; $ if (*fileName && !once++) {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org " "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n" "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n" "P.S. См. мое описание в TXLib Help."); } $ return INT_MIN; } $ bool async = false; if (*fileName == '\a') {$ async = true; fileName++; } $ RECT rect = {}; if (wnd) {$ GetClientRect (wnd, &rect); } if (!width) {$ width = rect.right; } if (!height) {$ height = rect.bottom; } // Create a child window to hold the video stream $ const char* errPos = "ВНЕЗАПНО"; $ volatile HWND child = NULL; $ if (wnd && (wnd == txWindow())) { $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1); $ static int number = 1; $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass }; $ child = txCreateExtraWindow (createData); $ if (!child) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos)); $ return INT_MIN+3; } $ BringWindowToTop (child); $ wnd = child; } // Build the command line if (!zoom && !wnd) {$ zoom = 1; } $ char sZoom [64] = "--autoscale"; if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; } $ static char cmd [MAX_PATH*2 + 1024] = ""; $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit" " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s" " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging" " --ignore-config --reset-config --no-one-instance --play-and-exit" " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file" " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events", vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted; $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n", x, y, width, height, fileName, zoom, gain, wnd, cmd); $ if (!*fileName) { if (child) {$ txDestroyWindow (child); } $ return (intptr_t) cmd; } $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0) { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos)); if (child) {$ txDestroyWindow (child); } $ return INT_MIN+1; } // Run VLC, run $ PROCESS_INFORMATION vlc = {}; $ STARTUPINFO start = { sizeof (start) }; $ DWORD ret = 0; $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) && vlc.hProcess && vlc.hThread) { $ if (child) { $ assert (wnd == child); $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess); } $ if (!async) { $ WaitForSingleObject (vlc.hProcess, INFINITE); $ GetExitCodeProcess (vlc.hProcess, &ret) asserted; } $ if (!child) { $ CloseHandle (vlc.hProcess) asserted; } $ CloseHandle (vlc.hThread) asserted; $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret); } else { $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s", strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos)); $ if (child) {$ txDestroyWindow (child); } $ return INT_MIN+4; } #undef PROCESS_UID_ } //----------------------------------------------------------------------------------------------------------------- inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/) { $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd); } //----------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { const UINT_PTR checkTimer = 1; switch (msg) { case WM_CREATE: { $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted; } break; case WM_DESTROY: { $1 KillTimer (wnd, checkTimer) asserted; $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); $ if (vlc) { $ Win32::TerminateProcess (vlc, 0); $ CloseHandle (vlc) asserted; $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0); } } break; case WM_TIMER: { HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA); if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT) { $1 DestroyWindow (wnd) asserted; } } break; default: break; } return DefWindowProc (wnd, msg, wpar, lpar); } //----------------------------------------------------------------------------------------------------------------- const char* _txPlayVideo_FindVLC() { $1 static char vlcPath [MAX_PATH] = ""; $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL)) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath))) { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath))) { $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX); if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX); { if (_access (vlcPath, 0) == 0) {$ return vlcPath; } } $ return NULL; } //----------------------------------------------------------------------------------------------------------------- // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :) // V Полезно смотреть не только вверх, но и вниз WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/) { $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc; $ return old; } //----------------------------------------------------------------------------------------------------------------- // +--<<< А это, наконец, искомое определение этой функции. // | Смотрите по сторонам! Нужная вам функция где-то рядом. // | // v bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture() { txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n" "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n" "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. " "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n" "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом " "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n" "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы " "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n", "Не получилось", MB_ICONSTOP); // The truth is out there... (C++files) return false; } //----------------------------------------------------------------------------------------------------------------- // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you. inline bool txDisableAutoPause() { _txExit = true; return true; } // P.S. This library contains more undocumented functions. Search them via "Luke" keyword. //----------------------------------------------------------------------------------------------------------------- void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/) { $1 assert (!_txIsBadReadPtr (address)); const unsigned char* p = (const unsigned char*) address; $ unsigned x = 0; $ unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_WHITE); $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : "")); $ txSetConsoleAttr (FOREGROUND_YELLOW); $ for (x = 0; x < 16; x++) printf ("%02X ", x); $ for (x = 0; x < 16; x++) printf ("%X", x); $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0"; $ const char xlatCh[] = "·" "·" "·" "·" "·" "·"; $ const size_t szCtrl = sizeof (isCtrl) - 1; $ int oldCP = GetConsoleOutputCP(); $ for (int y = 0; y < 16; y++, p += 16) { txSetConsoleAttr (FOREGROUND_YELLOW); printf ("\n" "%*p ", (int) sizeof (address) * 2, p); int color = FOREGROUND_LIGHTGREEN; for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); printf ("%02X ", p[x]); } for (x = 0; x < 16; x++) { txSetConsoleAttr (color + x/4%2); char c = p[x]; const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl); if (ctrl) c = xlatCh [ctrl - isCtrl]; if (ctrl) SetConsoleOutputCP (1251); printf ("%c", c); if (ctrl) SetConsoleOutputCP (oldCP); } } $ txSetConsoleAttr (attr); $ printf ("\n"); $ if (pause) { $ SetConsoleOutputCP (_TX_CODEPAGE); $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP); $ (void)_getch(); $ SetConsoleOutputCP (oldCP); } } //----------------------------------------------------------------------------------------------------------------- void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/, bool readSource /*= true*/) { $1 unsigned attr = txGetConsoleAttr(); $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN); $ fprintf (stderr, "\n" "--------------------------------------------------\n" "Трассировка стека из \"%s\" at %s (%d):\n\n" "%s\n\n" "--------------------------------------------------\n\n", func, file, line, _txCaptureStackBackTrace (1, readSource)); $ txSetConsoleAttr (attr); } //----------------------------------------------------------------------------------------------------------------- char* txDemangle (const char* mangledName, std::nomeow_t) { $1 if (!mangledName) return NULL; $ char* typeName = NULL; #if defined (_GCC_VER) $ int err = 1; $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err; if (typeName) {$ return typeName; } #endif $ unsigned short flags = 0; $ if (mangledName[0] == '.') { $ mangledName++; $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY } $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags)); if (typeName) {$ return typeName; } $ return _strdup (mangledName); } //----------------------------------------------------------------------------------------------------------------- std::string txDemangle (const char* mangledName) { $1 char* typeName = txDemangle (mangledName, std::nomeow); $ std::string name (typeName? typeName : ""); $ free (typeName); $ return name; } //----------------------------------------------------------------------------------------------------------------- double txQueryPerformance() { $1 int maxTime = 500; $ int maxSamples = 100; $ POINT size = {100, 100}; $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL); $ assert (dc); if (!dc) return -1; $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1); $ assert (mask); $ LARGE_INTEGER freq = {}; $ QueryPerformanceFrequency (&freq) asserted; $ LARGE_INTEGER start = {}; $ QueryPerformanceCounter (&start) asserted; $ int samples = 0; $ while (samples++ < maxSamples) { $ LARGE_INTEGER cur = {}; $ QueryPerformanceCounter (&cur) asserted; $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart; $ if (t > maxTime) break; // Draw test scene $ for (int y = 0; y < size.y; y++) for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc); $ for (int y = 0; y < size.y; y += 10) for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc); $ txEllipse (0, 0, size.x, size.y, dc); $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc); $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted; $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted; $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted; } $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask); $ assert (mask); $ _txBuffer_Delete (&dc); $ return 3.0 * samples / sqrt (1.0 * size.x * size.y); } //----------------------------------------------------------------------------------------------------------------- #if defined (_TX_CPP11) template <int txFramesToAverage> #endif double txGetFPS (int minFrames) { $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0); $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time); $ if (time.QuadPart - time0.QuadPart == 0) {$ return 0; } $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq); $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart); $ time0 = time; $ if (txFramesToAverage == 0) return fps; $ static double average [txFramesToAverage] = {}; $ static unsigned n = 0; $ average [n++ % txFramesToAverage] = fps; $ unsigned nn = MIN (n, (unsigned) sizearr (average)); $ static double median [txFramesToAverage] = {}; $ std::copy (average, average + nn, median); $ std::nth_element (median, median + nn/2, median + nn); $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0; $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0; } //----------------------------------------------------------------------------------------------------------------- unsigned txExtractColor (COLORREF color, COLORREF component) { $1 switch (component) { case TX_RED: case TX_HUE: $ return (color >> 0) & 0xFF; case TX_GREEN: case TX_SATURATION: $ return (color >> 8) & 0xFF; case TX_BLUE: case TX_LIGHTNESS: $ return (color >> 16) & 0xFF; default: $ return CLR_INVALID; } } //----------------------------------------------------------------------------------------------------------------- COLORREF txRGB2HSL (COLORREF rgbColor) { $1 int r = (int) txExtractColor (rgbColor, TX_RED), g = (int) txExtractColor (rgbColor, TX_GREEN), b = (int) txExtractColor (rgbColor, TX_BLUE); $ double m1 = MAX (MAX (r, g), b) / 255.0, m2 = MIN (MIN (r, g), b) / 255.0, dm = m1 - m2, sm = m1 + m2, ir = r / 255.0, ig = g / 255.0, ib = b / 255.0, ih = 0, is = 0, il = sm / 2; $ const double prec = 0.001; $ if (fabs (dm) < prec) { $ is = dm / ((sm <= 1)? sm : (2-sm)); $ double cr = (m1 - ir) / dm, cg = (m1 - ig) / dm, cb = (m1 - ib) / dm; $ if (fabs (ir - m1) < prec) ih = cb - cg; $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb; $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr; } $ ih = (ih >= 0)? ih*60 : ih*60 + 360; $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255)); } //----------------------------------------------------------------------------------------------------------------- COLORREF txHSL2RGB (COLORREF hslColor) { $1 struct xRGB { static double calc (double h, double m1, double m2) { $ if (h < 0) h += 360; $ if (h > 360) h -= 360; $ return (h < 60)? m1 + (m2-m1) * h / 60 : (h < 180)? m2 : (h < 240)? m1 + (m2-m1) * (240-h) / 60 : m1; } }; $ int h = (int) txExtractColor (hslColor, TX_HUE), s = (int) txExtractColor (hslColor, TX_SATURATION), l = (int) txExtractColor (hslColor, TX_LIGHTNESS); $ double ih = h / 255.0 * 360.0, il = l / 100.0, is = s / 100.0, m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is, m1 = 2 * il - m2, ir = s? xRGB::calc (ih + 120, m1, m2) : il, ig = s? xRGB::calc (ih, m1, m2) : il, ib = s? xRGB::calc (ih - 120, m1, m2) : il; $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255)); } //----------------------------------------------------------------------------------------------------------------- inline double random (std::nomeow_t, double left, double right) { return left + (right - left) * ((double) rand() / RAND_MAX); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (std::nomeow_t, Tx x, Ta a, Tb b) { return a <= x && x <= b; } //----------------------------------------------------------------------------------------------------------------- inline int random (int range) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return rand() % range; } //----------------------------------------------------------------------------------------------------------------- inline double random (double left, double right) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return random (std::nomeow, left, right); } //----------------------------------------------------------------------------------------------------------------- template <typename Tx, typename Ta, typename Tb> inline bool In (Tx x, Ta a, Tb b) { if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast)); return In (std::nomeow, x, a, b); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const POINT& pt, const RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.x, rect.left, rect.right) && In (std::nomeow, pt.y, rect.top, rect.bottom); } //----------------------------------------------------------------------------------------------------------------- inline bool In (const COORD& pt, const SMALL_RECT& rect) { if (_TX_ARGUMENT_FAILED (&pt)) return false; if (_TX_ARGUMENT_FAILED (&rect)) return false; return In (std::nomeow, pt.X, rect.Left, rect.Right) && In (std::nomeow, pt.Y, rect.Top, rect.Bottom); } //----------------------------------------------------------------------------------------------------------------- void tx_fpreset() { $1 txAutoLock _lock; $ Win32::_fpreset(); $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW #if !defined (__CYGWIN__) $ unsigned old87 = 0; $ if (_controlfp_s (&old87, 0, 0) == 0) {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM #else $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM #endif } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline T zero() { T __zero = {}; return __zero; } //} //================================================================================================================= //================================================================================================================= //{ txPrintf() implementation // Реализация txPrintf() //================================================================================================================= #if defined (_TX_CPP11) template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args); template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args); void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt); template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg); void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt); //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); } else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args) { $1 assert (&stream); $ assert (fmt); $ _txPrintV (stream, format, n, fmt); if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); } else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); } $ _txPrintV (stream, format, n, fmt, arg); $ _txPrintF (stream, format, n+1, fmt, args...); } //----------------------------------------------------------------------------------------------------------------- void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt) { $1 assert (fmt); $ _txPrintV (stream, format, n, fmt); if (!fmt[0]) {$} else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); } } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt) { $1 assert (&stream); $ assert (fmt); $ while (*fmt) { if (fmt[0] == '%') { if (fmt[1] == '%') fmt++; else break; } stream << *fmt++; } $ } //----------------------------------------------------------------------------------------------------------------- template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg) { $1 assert (&stream); $ assert (fmt); $ if (_TX_ARGUMENT_FAILED (&arg)) return; if (fmt[0] == '%') {$} else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); } $ fmt++; $ char oldFill = stream.fill (' '); $ std::ios_base::fmtflags oldFlags = stream.flags(); $ for (;;) switch (*fmt) { case '-': $ stream << std::left; fmt++; break; case '+': $ stream << std::showpos; fmt++; break; case ' ': $ stream.fill (' '); fmt++; break; case '#': $ stream << std::showbase; fmt++; break; case '0': $ stream.fill ('0'); fmt++; break; default: $ goto end; } end: $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0); $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0; if (width) {$ stream << std::setw (width); } if (prec) {$ stream << std::setprecision (prec); } $ fmt += strspn (fmt, "hljztL"); $ switch (*fmt) { case '$': case '?': $ break; case 'd': case 'i': case 'u': $ stream << std::dec; break; case 'o': $ stream << std::oct; break; case 'x': $ stream << std::hex; break; case 'X': $ stream << std::hex << std::uppercase; break; case 'f': $ stream << std::fixed; break; case 'F': $ stream << std::fixed << std::uppercase; break; case 'e': $ stream << std::scientific; break; case 'E': $ stream << std::scientific << std::uppercase; break; case 'g': $ break; case 'G': $ stream << std::uppercase; break; case 'a': $ break; case 'A': $ stream << std::uppercase; break; case 'c': case 's': case 'p': $ break; default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break; } $ fmt++; if (&arg) {$ stream << arg; } else {$ stream << "(null)"; } $ stream.fill (oldFill); $ stream.flags (oldFlags); } //----------------------------------------------------------------------------------------------------------------- void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg) { $1 assert (fmt); if (_TX_ARGUMENT_FAILED (arg)) return; if (fmt[0] == '%' && fmt[1] == 'n') {$} else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); } $ *arg = (int) stream.str().length(); $ fmt += 2; } //----------------------------------------------------------------------------------------------------------------- template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; } inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ const char* fmt = format; $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0; $ if (_TX_ARGUMENT_FAILED (&format)) return 0; $ if (size > 0) size--; $ buffer[size] = 0; $ if (!size) return 0; $ std::ostringstream stream; $ stream.rdbuf() -> pubsetbuf (buffer, size); $ txPrintf (stream, format, args...); $ return (int) stream.str().length(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline std::string txFormat (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return ""; $ std::ostringstream stream; $ txPrintf (stream, format, args...); $ return stream.str(); } //----------------------------------------------------------------------------------------------------------------- template <typename... ArgsT> inline int txPrintf (const char* format, ArgsT... args) { $1 if (_TX_ARGUMENT_FAILED (&format)) return 0; $ return printf ("%s", txFormat (format, args...) .c_str()); } #endif //----------------------------------------------------------------------------------------------------------------- int _txPrintfCheck (const char* format, ...) tx_printfy (1); inline int _txPrintfCheck (const char*, ...) { return 0; } //} //================================================================================================================= //================================================================================================================= //{ txDialog methods implementation // Реализация методов класса txDialog // // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753 //================================================================================================================= txDialog::txDialog () : layout_ (NULL) {$1} //----------------------------------------------------------------------------------------------------------------- txDialog::txDialog (const Layout* layout) : layout_ (layout) {$1} //----------------------------------------------------------------------------------------------------------------- const txDialog::Layout* txDialog::setLayout (const Layout* layout) { $1 assert (layout); $ return ::std::swap (layout_, layout), layout; } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (WORD resourceID) { $1 const char* resName = (char*)(uintptr_t) resourceID; $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0; $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this); } //----------------------------------------------------------------------------------------------------------------- intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/) { $1 if (!layout) layout = layout_; $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки
12941  // на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто.
12942  // Хотя это и сложнее.</s>
12943 
12944  // Но помни про паленый копипаст и карму, см. выше. Я предупредил.
12945 
12946 $ return ok;
12947  }
12948 
12949 //-----------------------------------------------------------------------------------------------------------------
12950 
12951 inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage,
12952  double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/)
12953  {
12954 $1 if (_TX_TXWINDOW_FAILED()) return false;
12955 
12956 $ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha);
12957  }
12958 
12959 //-----------------------------------------------------------------------------------------------------------------
12960 
12961 HDC txUseAlpha (HDC image)
12962  {
12963 $1 if (_TX_HDC_FAILED (image)) return NULL;
12964 
12965 $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP);
12966 $ if (!bitmap) return NULL;
12967 
12968 $ DIBSECTION dib = {};
12969 $ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted;
12970 
12971 $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight };
12972 $ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }};
12973 $ RGBQUAD* buf = NULL;
12974 
12975 $ bool isDIB = (dib.dsBm.bmPlanes == 1 &&
12976  dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 &&
12977  dib.dsBmih.biCompression == DIB_RGB_COLORS &&
12978  dib.dsBm.bmBits);
12979 $ if (!isDIB)
12980  {
12981 $ buf = new (std::nothrow) RGBQUAD [size.x * size.y];
12982 $ if (!buf) return NULL;
12983 
12984 $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted;
12985  }
12986  else
12987  {
12988 $ buf = (RGBQUAD*) dib.dsBm.bmBits;
12989  }
12990 
12991 $ for (int y = 0; y < size.y; y++)
12992  for (int x = 0; x < size.x; x++)
12993  {
12994  RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer
12995 
12996  color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0);
12997  color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0);
12998  color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0);
12999  }
13000 
13001 $ if (!isDIB)
13002  {
13003 $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted;
13004 
13005 $ delete[] buf;
13006  }
13007 
13008 $ return image;
13009  }
13010 
13011 //-----------------------------------------------------------------------------------------------------------------
13012 
13013 bool txSaveImage (const char filename[], HDC dc /*= txDC()*/)
13014  {
13015 $1 if (_TX_ARGUMENT_FAILED (filename)) return false;
13016 $ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
13017 
13018 $ POINT size = txGetExtent (dc);
13019 
13020 $ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER),
13021  szImg = (size.x * size.y) * sizeof (RGBQUAD);
13022 
13023 $ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs };
13024 $ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB };
13025 
13026 $ bool ok = true;
13027 
13028 $ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y];
13029 $ ok &= (buf != NULL);
13030 
13031 $ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y,
13032  buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted;
13033 $ FILE* f = NULL;
13034 $ if (ok) fopen_s (&f, filename, "wb");
13035 $ ok &= (f != NULL);
13036 
13037 $ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1);
13038 $ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1);
13039 $ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1);
13040 
13041 $ ok &= (f && fclose (f) == 0);
13042 
13043 $ delete[] buf;
13044 $ buf = NULL;
13045 
13046 $ return ok;
13047  }
13048 
13049 //-----------------------------------------------------------------------------------------------------------------
13050 
13051 inline void txRedrawWindow()
13052  {
13053 $1 txSleep (0);
13054  }
13055 
13056 //-----------------------------------------------------------------------------------------------------------------
13057 
13058 inline int txUpdateWindow (int update /*= true*/)
13059  {
13060 $1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update);
13061  }
13062 
13063 //-----------------------------------------------------------------------------------------------------------------
13064 
13065 inline int txBegin()
13066  {
13067 $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1);
13068 
13069 $ return _txCanvas_RefreshLock;
13070  }
13071 
13072 //-----------------------------------------------------------------------------------------------------------------
13073 
13074 inline int txEnd()
13075  {
13076 $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1);
13077 
13078 $ return _txCanvas_RefreshLock;
13079  }
13080 
13081 //-----------------------------------------------------------------------------------------------------------------
13082 
13083 double txSleep (double time)
13084  {
13085 $1 LARGE_INTEGER start = {};
13086 $ QueryPerformanceCounter (&start) asserted;
13087 
13088 $ LARGE_INTEGER freq = {};
13089 $ QueryPerformanceFrequency (&freq) asserted;
13090 
13091 $ int lock = _txCanvas_RefreshLock;
13092 $ _txCanvas_RefreshLock = 0;
13093 
13094 $ HWND wnd = txWindow();
13095  if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
13096 
13097 $ Sleep (ROUND ((time >= 0)? time : 0));
13098 
13099 $ _txCanvas_RefreshLock = lock;
13100 
13101 $ LARGE_INTEGER stop = {};
13102 $ QueryPerformanceCounter (&stop) asserted;
13103 
13104 $ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart;
13105  }
13106 
13107 //-----------------------------------------------------------------------------------------------------------------
13108 
13109 bool txLock (bool wait /*= true*/)
13110  {
13111 $0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
13112 
13113 $ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; }
13114  else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); }
13115  }
13116 
13117 //-----------------------------------------------------------------------------------------------------------------
13118 
13119 bool txUnlock()
13120  {
13121 $0 LeaveCriticalSection (&_txCanvas_LockBackBuf);
13122 
13123 $ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
13124 $ return false;
13125  }
13126 
13127 //-----------------------------------------------------------------------------------------------------------------
13128 
13129 template <typename T>
13130 inline T txUnlock (T value)
13131  {
13132 $1 txUnlock();
13133 $ return value;
13134  }
13135 
13136 //-----------------------------------------------------------------------------------------------------------------
13137 
13138 inline POINT txMousePos()
13139  {
13140 $1 POINT pos = {};
13141 $ GetCursorPos (&pos);
13142 
13143 $ if (txWindow())
13144  {$ ScreenToClient (txWindow(), &pos); }
13145 
13146 $ return pos;
13147  }
13148 
13149 //-----------------------------------------------------------------------------------------------------------------
13150 
13151 inline int txMouseX()
13152  {
13153  return txMousePos() .x;
13154  }
13155 
13156 //-----------------------------------------------------------------------------------------------------------------
13157 
13158 inline int txMouseY()
13159  {
13160  return txMousePos() .y;
13161  }
13162 
13163 //-----------------------------------------------------------------------------------------------------------------
13164 
13165 inline unsigned txMouseButtons()
13166  {
13167 $1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0
13168  ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1
13169  ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2
13170  }
13171 
13172 //-----------------------------------------------------------------------------------------------------------------
13173 
13174 unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/)
13175  {
13176  unsigned oldAttr = txGetConsoleAttr();
13177 
13178  SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color);
13179 
13180  return oldAttr;
13181  }
13182 
13183 //-----------------------------------------------------------------------------------------------------------------
13184 
13185 unsigned txGetConsoleAttr()
13186  {
13187  CONSOLE_SCREEN_BUFFER_INFO con = {};
13188  GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
13189 
13190  return con.wAttributes;
13191  }
13192 
13193 //-----------------------------------------------------------------------------------------------------------------
13194 
13195 POINT txSetConsoleCursorPos (double x, double y)
13196  {
13197 $1 POINT fontSz = txGetConsoleFontSize();
13198 
13199 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
13200 $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
13201 
13202 $ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left),
13203  (short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) };
13204 
13205 $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted;
13206 
13207 $ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
13208  ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
13209 $ return prev;
13210  }
13211 
13212 //-----------------------------------------------------------------------------------------------------------------
13213 
13214 POINT txGetConsoleCursorPos()
13215  {
13216 $1 POINT fontSz = txGetConsoleFontSize();
13217 
13218 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
13219 $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
13220 
13221 $ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
13222  ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
13223 $ return pos;
13224  }
13225 
13226 //-----------------------------------------------------------------------------------------------------------------
13227 
13228 POINT txGetConsoleExtent()
13229  {
13230 $1 CONSOLE_SCREEN_BUFFER_INFO con = {};
13231 $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
13232 
13233 $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1,
13234  con.srWindow.Bottom - con.srWindow.Top + 1 };
13235 $ return size;
13236  }
13237 
13238 //-----------------------------------------------------------------------------------------------------------------
13239 
13240 bool txClearConsole()
13241  {
13242 $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
13243 
13244 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
13245 $ GetConsoleScreenBufferInfo (out, &con) asserted;
13246 
13247 $ COORD start = {con.srWindow.Left, con.srWindow.Top};
13248 
13249 $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) *
13250  (con.srWindow.Bottom - con.srWindow.Top + 1);
13251 
13252 $ DWORD written = 0;
13253 $ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted;
13254 $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted;
13255 
13256 $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted;
13257 
13258 $ return written == len;
13259  }
13260 
13261 //-----------------------------------------------------------------------------------------------------------------
13262 
13263 POINT txGetConsoleFontSize()
13264  {
13265 $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}};
13266 
13267 $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font));
13268 
13269 $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y };
13270 $ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC());
13271 
13272 $ POINT sizeFont = { size.cx, size.cy };
13273 $ return sizeFont;
13274  }
13275 
13276 //-----------------------------------------------------------------------------------------------------------------
13277 
13278 bool txTextCursor (bool blink /*= true*/)
13279  {
13280 $1 bool old = _txConsole_IsBlinking;
13281 
13282 $ _txConsole_IsBlinking = blink;
13283 
13284 $ return old;
13285  }
13286 
13287 //-----------------------------------------------------------------------------------------------------------------
13288 
13289 bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/)
13290  {
13291 $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT;
13292 $ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC;
13293 
13294 $ if (!filename) mode = SND_PURGE;
13295 
13296 $ return !!Win32::PlaySound (filename, NULL, mode);
13297  }
13298 
13299 //-----------------------------------------------------------------------------------------------------------------
13300 
13301 int txSpeak (const char* text, ...)
13302  {
13303 $1 bool verbose = false; (void) verbose;
13304 $ bool async = false; (void) async;
13305 
13306 $ for (; text && *text; text++)
13307  {
13308  if (*text == '\a') {$ async = true; }
13309  else if (*text == '\v') {$ verbose = true; }
13310  else break;
13311  }
13312 
13313 $ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!";
13314 
13315 $ va_list arg; va_start (arg, text);
13316  if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); }
13317 $ va_end (arg);
13318 
13319 #ifdef TX_USE_SPEAK
13320 
13321  if (text && verbose) {$ printf ("%s", textA); }
13322 
13323 $ int time = GetTickCount();
13324 
13325 $ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L"";
13326 $ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW));
13327 
13328 $ static ISpVoice* voice = NULL;
13329 
13330 $ if (text && !voice)
13331  {
13332 $ HRESULT res = Win32::CoInitialize (NULL);
13333  if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); }
13334  }
13335 
13336 $ if (text && voice)
13337  {
13338 $ Win32::_fpreset();
13339 $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL);
13340 $ tx_fpreset();
13341  }
13342 
13343 $ if (!text && voice)
13344  {
13345 $ voice->Release();
13346 $ voice = NULL;
13347 
13348 $ Win32::CoUninitialize();
13349  }
13350 
13351 $ return (voice)? GetTickCount() - time : -1;
13352 
13353 #else
13354 
13355 $ if (text)
13356  {
13357 $ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK);
13358 
13359 $ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA);
13360 
13361 $ txSetConsoleAttr (oldAttr);
13362  }
13363 
13364 $ return -1;
13365 
13366 #endif
13367  }
13368 
13369 //-----------------------------------------------------------------------------------------------------------------
13370 
13371 intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[],
13372  double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/)
13373  {
13374 $1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1;
13375 
13376 $ int time = GetTickCount();
13377 
13378 $ static char processUID [64] = "";
13379  if (!*processUID)
13380  {
13381 $ FILETIME startTime = {}, null = {};
13382 $ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted;
13383 $ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo",
13384  (unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted;
13385  }
13386 
13387 $ if (!fileName)
13388  {
13389 $ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern
13390 $ return 0;
13391  }
13392 
13393 $ static const char* vlcPath = _txPlayVideo_FindVLC();
13394 
13395 $ if (!vlcPath || _access (vlcPath, 0) != 0)
13396  {
13397 $ static int once = false;
13398 
13399 $ if (*fileName && !once++)
13400  {$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org "
13401  "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n"
13402  "--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n"
13403  "P.S. См. мое описание в TXLib Help."); }
13404 $ return INT_MIN;
13405  }
13406 
13407 $ bool async = false;
13408  if (*fileName == '\a') {$ async = true; fileName++; }
13409 
13410 $ RECT rect = {};
13411  if (wnd) {$ GetClientRect (wnd, &rect); }
13412 
13413  if (!width) {$ width = rect.right; }
13414  if (!height) {$ height = rect.bottom; }
13415 
13416  // Create a child window to hold the video stream
13417 
13418 $ const char* errPos = "ВНЕЗАПНО";
13419 
13420 $ volatile HWND child = NULL;
13421 $ if (wnd && (wnd == txWindow()))
13422  {
13423 $ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1);
13424 
13425 $ static int number = 1;
13426 $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x,
13427  WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass };
13428 $ child = txCreateExtraWindow (createData);
13429 $ if (!child)
13430  {
13431 $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s",
13432  strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos));
13433 $ return INT_MIN+3;
13434  }
13435 
13436 $ BringWindowToTop (child);
13437 
13438 $ wnd = child;
13439  }
13440 
13441  // Build the command line
13442 
13443  if (!zoom && !wnd) {$ zoom = 1; }
13444 
13445 $ char sZoom [64] = "--autoscale";
13446  if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; }
13447 
13448 $ static char cmd [MAX_PATH*2 + 1024] = "";
13449 
13450 $ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit"
13451 
13452  " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s"
13453 
13454  " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging"
13455 
13456  " --ignore-config --reset-config --no-one-instance --play-and-exit"
13457  " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file"
13458  " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events",
13459 
13460  vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted;
13461 
13462 $ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n",
13463  x, y, width, height, fileName, zoom, gain, wnd, cmd);
13464 $ if (!*fileName)
13465  {
13466  if (child) {$ txDestroyWindow (child); }
13467 $ return (intptr_t) cmd;
13468  }
13469 
13470 $ if (!strstr (fileName, "://") && _access (fileName, 0) != 0)
13471  {
13472 $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s",
13473  strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos));
13474 
13475  if (child) {$ txDestroyWindow (child); }
13476 $ return INT_MIN+1;
13477  }
13478 
13479  // Run VLC, run
13480 
13481 $ PROCESS_INFORMATION vlc = {};
13482 $ STARTUPINFO start = { sizeof (start) };
13483 $ DWORD ret = 0;
13484 
13485 $ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) &&
13486  vlc.hProcess && vlc.hThread)
13487  {
13488 $ if (child)
13489  {
13490 $ assert (wnd == child);
13491 $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess);
13492  }
13493 
13494 $ if (!async)
13495  {
13496 $ WaitForSingleObject (vlc.hProcess, INFINITE);
13497 $ GetExitCodeProcess (vlc.hProcess, &ret) asserted;
13498  }
13499 
13500 $ if (!child)
13501  {
13502 $ CloseHandle (vlc.hProcess) asserted;
13503  }
13504 
13505 $ CloseHandle (vlc.hThread) asserted;
13506 
13507 $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret);
13508  }
13509  else
13510  {
13511 $ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s",
13512  strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos));
13513 $ if (child)
13514  {$ txDestroyWindow (child); }
13515 
13516 $ return INT_MIN+4;
13517  }
13518 
13519  #undef PROCESS_UID_
13520  }
13521 
13522 //-----------------------------------------------------------------------------------------------------------------
13523 
13524 inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/)
13525  {
13526 $1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd);
13527  }
13528 
13529 //-----------------------------------------------------------------------------------------------------------------
13530 
13531 LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
13532  {
13533  const UINT_PTR checkTimer = 1;
13534 
13535  switch (msg)
13536  {
13537  case WM_CREATE:
13538  {
13539 $1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted;
13540  }
13541  break;
13542 
13543  case WM_DESTROY:
13544  {
13545 $1 KillTimer (wnd, checkTimer) asserted;
13546 
13547 $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
13548 
13549 $ if (vlc)
13550  {
13551 $ Win32::TerminateProcess (vlc, 0);
13552 
13553 $ CloseHandle (vlc) asserted;
13554 
13555 $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0);
13556  }
13557  }
13558  break;
13559 
13560  case WM_TIMER:
13561  {
13562  HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
13563 
13564  if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT)
13565  {
13566 $1 DestroyWindow (wnd) asserted;
13567  }
13568  }
13569  break;
13570 
13571  default:
13572  break;
13573  }
13574 
13575  return DefWindowProc (wnd, msg, wpar, lpar);
13576  }
13577 
13578 //-----------------------------------------------------------------------------------------------------------------
13579 
13580 const char* _txPlayVideo_FindVLC()
13581  {
13582 $1 static char vlcPath [MAX_PATH] = "";
13583 
13584 $ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL))
13585  {
13586  if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
13587  }
13588 
13589 $ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL))
13590  {
13591  if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
13592  }
13593 
13594 $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath)))
13595  {
13596  if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
13597  }
13598 
13599 $ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath)))
13600  {
13601 $ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX);
13602 
13603  if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
13604  }
13605 
13606 $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
13607  {
13608  if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
13609  }
13610 
13611 $ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
13612  {
13613  if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
13614  }
13615 
13616 $ return NULL;
13617  }
13618 
13619 //-----------------------------------------------------------------------------------------------------------------
13620 
13621 // +--<<< Это вряд ли имеет отношение к тому, что вы ищете :)
13622 // V Полезно смотреть не только вверх, но и вниз
13623 
13624 WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/)
13625  {
13626 $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc;
13627 $ return old;
13628  }
13629 
13630 //-----------------------------------------------------------------------------------------------------------------
13631 
13632 // +--<<< А это, наконец, искомое определение этой функции.
13633 // | Смотрите по сторонам! Нужная вам функция где-то рядом.
13634 // |
13635 // v
13637  {
13638  txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n"
13639 
13640  "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n"
13641 
13642  "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. "
13643  "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n"
13644 
13645  "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом "
13646  "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n"
13647 
13648  "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы "
13649  "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n",
13650 
13651  "Не получилось", MB_ICONSTOP);
13652 
13653  // The truth is out there... (C++files)
13654 
13655  return false;
13656  }
13657 
13658 //-----------------------------------------------------------------------------------------------------------------
13659 
13660 // Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you.
13661 
13662 inline bool txDisableAutoPause()
13663  {
13664  _txExit = true;
13665  return true;
13666  }
13667 
13668 // P.S. This library contains more undocumented functions. Search them via "Luke" keyword.
13669 
13670 //-----------------------------------------------------------------------------------------------------------------
13671 
13672 void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/)
13673  {
13674 $1 assert (!_txIsBadReadPtr (address));
13675 
13676  const unsigned char* p = (const unsigned char*) address;
13677 $ unsigned x = 0;
13678 
13679 $ unsigned attr = txGetConsoleAttr();
13680 
13681 $ txSetConsoleAttr (FOREGROUND_WHITE);
13682 $ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : ""));
13683 
13684 $ txSetConsoleAttr (FOREGROUND_YELLOW);
13685 $ for (x = 0; x < 16; x++) printf ("%02X ", x);
13686 $ for (x = 0; x < 16; x++) printf ("%X", x);
13687 
13688 $ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0";
13689 $ const char xlatCh[] = "·" "·" "·" "·" "·" "·";
13690 $ const size_t szCtrl = sizeof (isCtrl) - 1;
13691 
13692 $ int oldCP = GetConsoleOutputCP();
13693 
13694 $ for (int y = 0; y < 16; y++, p += 16)
13695  {
13696  txSetConsoleAttr (FOREGROUND_YELLOW);
13697 
13698  printf ("\n" "%*p ", (int) sizeof (address) * 2, p);
13699 
13700  int color = FOREGROUND_LIGHTGREEN;
13701 
13702  for (x = 0; x < 16; x++)
13703  {
13704  txSetConsoleAttr (color + x/4%2);
13705  printf ("%02X ", p[x]);
13706  }
13707 
13708  for (x = 0; x < 16; x++)
13709  {
13710  txSetConsoleAttr (color + x/4%2);
13711 
13712  char c = p[x];
13713  const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl);
13714  if (ctrl) c = xlatCh [ctrl - isCtrl];
13715 
13716  if (ctrl) SetConsoleOutputCP (1251);
13717 
13718  printf ("%c", c);
13719 
13720  if (ctrl) SetConsoleOutputCP (oldCP);
13721  }
13722  }
13723 
13724 $ txSetConsoleAttr (attr);
13725 $ printf ("\n");
13726 
13727 $ if (pause)
13728  {
13729 $ SetConsoleOutputCP (_TX_CODEPAGE);
13730 
13731 $ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP);
13732 $ (void)_getch();
13733 
13734 $ SetConsoleOutputCP (oldCP);
13735  }
13736  }
13737 
13738 //-----------------------------------------------------------------------------------------------------------------
13739 
13740 void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/,
13741  bool readSource /*= true*/)
13742  {
13743 $1 unsigned attr = txGetConsoleAttr();
13744 $ txSetConsoleAttr (FOREGROUND_LIGHTCYAN);
13745 
13746 $ fprintf (stderr, "\n" "--------------------------------------------------\n"
13747  "Трассировка стека из \"%s\" at %s (%d):\n\n"
13748  "%s\n\n"
13749  "--------------------------------------------------\n\n",
13750  func, file, line, _txCaptureStackBackTrace (1, readSource));
13751 
13752 $ txSetConsoleAttr (attr);
13753  }
13754 
13755 //-----------------------------------------------------------------------------------------------------------------
13756 
13757 char* txDemangle (const char* mangledName, std::nomeow_t)
13758  {
13759 $1 if (!mangledName) return NULL;
13760 
13761 $ char* typeName = NULL;
13762 
13763  #if defined (_GCC_VER)
13764 
13765 $ int err = 1;
13766 $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err;
13767  if (typeName) {$ return typeName; }
13768 
13769  #endif
13770 
13771 $ unsigned short flags = 0;
13772 
13773 $ if (mangledName[0] == '.')
13774  {
13775 $ mangledName++;
13776 $ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY
13777  }
13778 
13779 $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags));
13780  if (typeName) {$ return typeName; }
13781 
13782 $ return _strdup (mangledName);
13783  }
13784 
13785 //-----------------------------------------------------------------------------------------------------------------
13786 
13787 std::string txDemangle (const char* mangledName)
13788  {
13789 $1 char* typeName = txDemangle (mangledName, std::nomeow);
13790 $ std::string name (typeName? typeName : "");
13791 $ free (typeName);
13792 
13793 $ return name;
13794  }
13795 
13796 //-----------------------------------------------------------------------------------------------------------------
13797 
13798 double txQueryPerformance()
13799  {
13800 $1 int maxTime = 500;
13801 $ int maxSamples = 100;
13802 $ POINT size = {100, 100};
13803 
13804 $ HDC dc = _txBuffer_Create (txWindow(), &size, NULL);
13805 $ assert (dc); if (!dc) return -1;
13806 
13807 $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1);
13808 $ assert (mask);
13809 
13810 $ LARGE_INTEGER freq = {};
13811 $ QueryPerformanceFrequency (&freq) asserted;
13812 
13813 $ LARGE_INTEGER start = {};
13814 $ QueryPerformanceCounter (&start) asserted;
13815 
13816 $ int samples = 0;
13817 $ while (samples++ < maxSamples)
13818  {
13819 $ LARGE_INTEGER cur = {};
13820 $ QueryPerformanceCounter (&cur) asserted;
13821 
13822 $ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart;
13823 $ if (t > maxTime) break;
13824 
13825  // Draw test scene
13826 
13827 $ for (int y = 0; y < size.y; y++)
13828  for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc);
13829 
13830 $ for (int y = 0; y < size.y; y += 10)
13831  for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc);
13832 
13833 $ txEllipse (0, 0, size.x, size.y, dc);
13834 $ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc);
13835 
13836 $ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted;
13837 $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted;
13838 $ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted;
13839 $ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted;
13840  }
13841 
13842 $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask);
13843 $ assert (mask);
13844 
13845 $ _txBuffer_Delete (&dc);
13846 
13847 $ return 3.0 * samples / sqrt (1.0 * size.x * size.y);
13848  }
13849 
13850 //-----------------------------------------------------------------------------------------------------------------
13851 
13852 #if defined (_TX_CPP11)
13853 template <int txFramesToAverage>
13854 #endif
13855 
13856 double txGetFPS (int minFrames)
13857  {
13858 $1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0);
13859 $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time);
13860 
13861 $ if (time.QuadPart - time0.QuadPart == 0)
13862  {$ return 0; }
13863 
13864 $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq);
13865 
13866 $ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart);
13867 $ time0 = time;
13868 
13869 $ if (txFramesToAverage == 0) return fps;
13870 
13871 $ static double average [txFramesToAverage] = {};
13872 $ static unsigned n = 0;
13873 
13874 $ average [n++ % txFramesToAverage] = fps;
13875 
13876 $ unsigned nn = MIN (n, (unsigned) sizearr (average));
13877 
13878 $ static double median [txFramesToAverage] = {};
13879 $ std::copy (average, average + nn, median);
13880 $ std::nth_element (median, median + nn/2, median + nn);
13881 
13882 $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0;
13883 
13884 $ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0;
13885  }
13886 
13887 //-----------------------------------------------------------------------------------------------------------------
13888 
13889 unsigned txExtractColor (COLORREF color, COLORREF component)
13890  {
13891 $1 switch (component)
13892  {
13893  case TX_RED:
13894  case TX_HUE: $ return (color >> 0) & 0xFF;
13895 
13896  case TX_GREEN:
13897  case TX_SATURATION: $ return (color >> 8) & 0xFF;
13898 
13899  case TX_BLUE:
13900  case TX_LIGHTNESS: $ return (color >> 16) & 0xFF;
13901 
13902  default: $ return CLR_INVALID;
13903  }
13904  }
13905 
13906 //-----------------------------------------------------------------------------------------------------------------
13907 
13908 COLORREF txRGB2HSL (COLORREF rgbColor)
13909  {
13910 $1 int r = (int) txExtractColor (rgbColor, TX_RED),
13911  g = (int) txExtractColor (rgbColor, TX_GREEN),
13912  b = (int) txExtractColor (rgbColor, TX_BLUE);
13913 
13914 $ double m1 = MAX (MAX (r, g), b) / 255.0,
13915  m2 = MIN (MIN (r, g), b) / 255.0,
13916  dm = m1 - m2,
13917  sm = m1 + m2,
13918 
13919  ir = r / 255.0,
13920  ig = g / 255.0,
13921  ib = b / 255.0,
13922 
13923  ih = 0,
13924  is = 0,
13925  il = sm / 2;
13926 
13927 $ const double prec = 0.001;
13928 
13929 $ if (fabs (dm) < prec)
13930  {
13931 $ is = dm / ((sm <= 1)? sm : (2-sm));
13932 
13933 $ double cr = (m1 - ir) / dm,
13934  cg = (m1 - ig) / dm,
13935  cb = (m1 - ib) / dm;
13936 
13937 $ if (fabs (ir - m1) < prec) ih = cb - cg;
13938 $ if (fabs (ig - m1) < prec) ih = 2 + cr - cb;
13939 $ if (fabs (ib - m1) < prec) ih = 4 + cg - cr;
13940  }
13941 
13942 $ ih = (ih >= 0)? ih*60 : ih*60 + 360;
13943 
13944 $ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255));
13945  }
13946 
13947 //-----------------------------------------------------------------------------------------------------------------
13948 
13949 COLORREF txHSL2RGB (COLORREF hslColor)
13950  {
13951 $1 struct xRGB
13952  {
13953  static double calc (double h, double m1, double m2)
13954  {
13955 $ if (h < 0) h += 360;
13956 $ if (h > 360) h -= 360;
13957 
13958 $ return (h < 60)? m1 + (m2-m1) * h / 60 :
13959  (h < 180)? m2 :
13960  (h < 240)? m1 + (m2-m1) * (240-h) / 60 :
13961  m1;
13962  }
13963  };
13964 
13965 $ int h = (int) txExtractColor (hslColor, TX_HUE),
13966  s = (int) txExtractColor (hslColor, TX_SATURATION),
13967  l = (int) txExtractColor (hslColor, TX_LIGHTNESS);
13968 
13969 $ double ih = h / 255.0 * 360.0,
13970  il = l / 100.0,
13971  is = s / 100.0,
13972 
13973  m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is,
13974  m1 = 2 * il - m2,
13975 
13976  ir = s? xRGB::calc (ih + 120, m1, m2) : il,
13977  ig = s? xRGB::calc (ih, m1, m2) : il,
13978  ib = s? xRGB::calc (ih - 120, m1, m2) : il;
13979 
13980 $ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255));
13981  }
13982 
13983 //-----------------------------------------------------------------------------------------------------------------
13984 
13985 inline double random (std::nomeow_t, double left, double right)
13986  {
13987  return left + (right - left) * ((double) rand() / RAND_MAX);
13988  }
13989 
13990 //-----------------------------------------------------------------------------------------------------------------
13991 
13992 template <typename Tx, typename Ta, typename Tb>
13993 inline bool In (std::nomeow_t, Tx x, Ta a, Tb b)
13994  {
13995  return a <= x && x <= b;
13996  }
13997 
13998 //-----------------------------------------------------------------------------------------------------------------
13999 
14000 inline int random (int range)
14001  {
14002  if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
14003 
14004  return rand() % range;
14005  }
14006 
14007 //-----------------------------------------------------------------------------------------------------------------
14008 
14009 inline double random (double left, double right)
14010  {
14011  if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
14012 
14013  return random (std::nomeow, left, right);
14014  }
14015 
14016 //-----------------------------------------------------------------------------------------------------------------
14017 
14018 template <typename Tx, typename Ta, typename Tb>
14019 inline bool In (Tx x, Ta a, Tb b)
14020  {
14021  if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
14022 
14023  return In (std::nomeow, x, a, b);
14024  }
14025 
14026 //-----------------------------------------------------------------------------------------------------------------
14027 
14028 inline bool In (const POINT& pt, const RECT& rect)
14029  {
14030  if (_TX_ARGUMENT_FAILED (&pt)) return false;
14031  if (_TX_ARGUMENT_FAILED (&rect)) return false;
14032 
14033  return In (std::nomeow, pt.x, rect.left, rect.right) &&
14034  In (std::nomeow, pt.y, rect.top, rect.bottom);
14035  }
14036 
14037 //-----------------------------------------------------------------------------------------------------------------
14038 
14039 inline bool In (const COORD& pt, const SMALL_RECT& rect)
14040  {
14041  if (_TX_ARGUMENT_FAILED (&pt)) return false;
14042  if (_TX_ARGUMENT_FAILED (&rect)) return false;
14043 
14044  return In (std::nomeow, pt.X, rect.Left, rect.Right) &&
14045  In (std::nomeow, pt.Y, rect.Top, rect.Bottom);
14046  }
14047 
14048 //-----------------------------------------------------------------------------------------------------------------
14049 
14050 void tx_fpreset()
14051  {
14052 $1 txAutoLock _lock;
14053 
14054 $ Win32::_fpreset();
14055 
14056 $ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW
14057 
14058  #if !defined (__CYGWIN__)
14059 
14060 $ unsigned old87 = 0;
14061 $ if (_controlfp_s (&old87, 0, 0) == 0)
14062  {$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM
14063 
14064  #else
14065 
14066 $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM
14067 
14068  #endif
14069  }
14070 
14071 //-----------------------------------------------------------------------------------------------------------------
14072 
14073 template <typename T>
14074 inline T zero() { T __zero = {}; return __zero; }
14075 
14076 //}
14077 //=================================================================================================================
14078 
14079 //=================================================================================================================
14080 //{ txPrintf() implementation
14081 // Реализация txPrintf()
14082 //=================================================================================================================
14083 
14084 #if defined (_TX_CPP11)
14085 
14086 template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args);
14087 template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args);
14088 template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args);
14089 template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args);
14090  void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt);
14091 
14092 template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg);
14093  void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg);
14094  void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt);
14095 
14096 //-----------------------------------------------------------------------------------------------------------------
14097 
14098 template <typename T, typename... ArgsT>
14099 void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args)
14100  {
14101 $1 assert (fmt);
14102 
14103 $ _txPrintV (stream, format, n, fmt);
14104 
14105  if (fmt[0] == '%') {$}
14106  else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); }
14107 
14108 $ _txPrintV (stream, format, n, fmt, arg);
14109 
14110 $ _txPrintF (stream, format, n+1, fmt, args...);
14111  }
14112 
14113 //-----------------------------------------------------------------------------------------------------------------
14114 
14115 template <typename T, typename... ArgsT>
14116 void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args)
14117  {
14118 $1 assert (&stream);
14119 $ assert (fmt);
14120 
14121 $ _txPrintV (stream, format, n, fmt);
14122 
14123  if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); }
14124  else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); }
14125 
14126 $ _txPrintV (stream, format, n, fmt, arg);
14127 
14128 $ _txPrintF (stream, format, n+1, fmt, args...);
14129  }
14130 
14131 //-----------------------------------------------------------------------------------------------------------------
14132 
14133 template <typename T, typename... ArgsT>
14134 void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args)
14135  {
14136 $1 assert (&stream);
14137 $ assert (fmt);
14138 
14139 $ _txPrintV (stream, format, n, fmt);
14140 
14141  if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); }
14142  else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); }
14143 
14144 $ _txPrintV (stream, format, n, fmt, arg);
14145 
14146 $ _txPrintF (stream, format, n+1, fmt, args...);
14147  }
14148 
14149 //-----------------------------------------------------------------------------------------------------------------
14150 
14151 template <typename T, typename... ArgsT>
14152 void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args)
14153  {
14154 $1 assert (&stream);
14155 $ assert (fmt);
14156 
14157 $ _txPrintV (stream, format, n, fmt);
14158 
14159  if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); }
14160  else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); }
14161 
14162 $ _txPrintV (stream, format, n, fmt, arg);
14163 
14164 $ _txPrintF (stream, format, n+1, fmt, args...);
14165  }
14166 
14167 //-----------------------------------------------------------------------------------------------------------------
14168 
14169 void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt)
14170  {
14171 $1 assert (fmt);
14172 
14173 $ _txPrintV (stream, format, n, fmt);
14174 
14175  if (!fmt[0]) {$}
14176  else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); }
14177  }
14178 
14179 //-----------------------------------------------------------------------------------------------------------------
14180 
14181 void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt)
14182  {
14183 $1 assert (&stream);
14184 $ assert (fmt);
14185 
14186 $ while (*fmt)
14187  {
14188  if (fmt[0] == '%')
14189  {
14190  if (fmt[1] == '%') fmt++;
14191  else break;
14192  }
14193 
14194  stream << *fmt++;
14195  }
14196 $ }
14197 
14198 //-----------------------------------------------------------------------------------------------------------------
14199 
14200 template <typename T>
14201 void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg)
14202  {
14203 $1 assert (&stream);
14204 $ assert (fmt);
14205 
14206 $ if (_TX_ARGUMENT_FAILED (&arg)) return;
14207 
14208  if (fmt[0] == '%') {$}
14209  else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); }
14210 
14211 $ fmt++;
14212 
14213 $ char oldFill = stream.fill (' ');
14214 $ std::ios_base::fmtflags oldFlags = stream.flags();
14215 
14216 $ for (;;) switch (*fmt)
14217  {
14218  case '-': $ stream << std::left; fmt++; break;
14219  case '+': $ stream << std::showpos; fmt++; break;
14220  case ' ': $ stream.fill (' '); fmt++; break;
14221  case '#': $ stream << std::showbase; fmt++; break;
14222  case '0': $ stream.fill ('0'); fmt++; break;
14223 
14224  default: $ goto end;
14225  }
14226  end:
14227 
14228 $ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0);
14229 $ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0;
14230 
14231  if (width) {$ stream << std::setw (width); }
14232  if (prec) {$ stream << std::setprecision (prec); }
14233 
14234 $ fmt += strspn (fmt, "hljztL");
14235 
14236 $ switch (*fmt)
14237  {
14238  case '$':
14239  case '?': $ break;
14240 
14241  case 'd':
14242  case 'i':
14243  case 'u': $ stream << std::dec; break;
14244 
14245  case 'o': $ stream << std::oct; break;
14246 
14247  case 'x': $ stream << std::hex; break;
14248  case 'X': $ stream << std::hex << std::uppercase; break;
14249 
14250  case 'f': $ stream << std::fixed; break;
14251  case 'F': $ stream << std::fixed << std::uppercase; break;
14252 
14253  case 'e': $ stream << std::scientific; break;
14254  case 'E': $ stream << std::scientific << std::uppercase; break;
14255 
14256  case 'g': $ break;
14257  case 'G': $ stream << std::uppercase; break;
14258 
14259  case 'a': $ break;
14260  case 'A': $ stream << std::uppercase; break;
14261 
14262  case 'c':
14263  case 's':
14264  case 'p': $ break;
14265 
14266  default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break;
14267  }
14268 
14269 $ fmt++;
14270 
14271  if (&arg) {$ stream << arg; }
14272  else {$ stream << "(null)"; }
14273 
14274 $ stream.fill (oldFill);
14275 $ stream.flags (oldFlags);
14276  }
14277 
14278 //-----------------------------------------------------------------------------------------------------------------
14279 
14280 void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg)
14281  {
14282 $1 assert (fmt);
14283 
14284  if (_TX_ARGUMENT_FAILED (arg)) return;
14285 
14286  if (fmt[0] == '%' && fmt[1] == 'n') {$}
14287  else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); }
14288 
14289 $ *arg = (int) stream.str().length();
14290 
14291 $ fmt += 2;
14292  }
14293 
14294 //-----------------------------------------------------------------------------------------------------------------
14295 
14296 template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; }
14297  inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); }
14298 
14299 //-----------------------------------------------------------------------------------------------------------------
14300 
14301 template <typename... ArgsT>
14302 inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args)
14303  {
14304 $1 if (_TX_ARGUMENT_FAILED (&stream)) return 0;
14305 $ if (_TX_ARGUMENT_FAILED (&format)) return 0;
14306 
14307 $ const char* fmt = format;
14308 $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...);
14309 
14310 $ return (int) stream.str().length();
14311  }
14312 
14313 //-----------------------------------------------------------------------------------------------------------------
14314 
14315 template <typename... ArgsT>
14316 inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args)
14317  {
14318 $1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0;
14319 $ if (_TX_ARGUMENT_FAILED (&format)) return 0;
14320 
14321 $ if (size > 0) size--;
14322 $ buffer[size] = 0;
14323 
14324 $ if (!size) return 0;
14325 
14326 $ std::ostringstream stream;
14327 $ stream.rdbuf() -> pubsetbuf (buffer, size);
14328 
14329 $ txPrintf (stream, format, args...);
14330 
14331 $ return (int) stream.str().length();
14332  }
14333 
14334 //-----------------------------------------------------------------------------------------------------------------
14335 
14336 template <typename... ArgsT>
14337 inline std::string txFormat (const char* format, ArgsT... args)
14338  {
14339 $1 if (_TX_ARGUMENT_FAILED (&format)) return "";
14340 
14341 $ std::ostringstream stream;
14342 
14343 $ txPrintf (stream, format, args...);
14344 
14345 $ return stream.str();
14346  }
14347 
14348 //-----------------------------------------------------------------------------------------------------------------
14349 
14350 template <typename... ArgsT>
14351 inline int txPrintf (const char* format, ArgsT... args)
14352  {
14353 $1 if (_TX_ARGUMENT_FAILED (&format)) return 0;
14354 
14355 $ return printf ("%s", txFormat (format, args...) .c_str());
14356  }
14357 
14358 #endif
14359 
14360 //-----------------------------------------------------------------------------------------------------------------
14361 
14362  int _txPrintfCheck (const char* format, ...) tx_printfy (1);
14363 inline int _txPrintfCheck (const char*, ...) { return 0; }
14364 
14365 //}
14366 //=================================================================================================================
14367 
14368 //=================================================================================================================
14369 //{ txDialog methods implementation
14370 // Реализация методов класса txDialog
14371 //
14372 // See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx
14373 // [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743
14374 // [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753
14375 //=================================================================================================================
14376 
14377 txDialog::txDialog () :
14378  layout_ (NULL)
14379  {$1}
14380 
14381 //-----------------------------------------------------------------------------------------------------------------
14382 
14383 txDialog::txDialog (const Layout* layout) :
14384  layout_ (layout)
14385  {$1}
14386 
14387 //-----------------------------------------------------------------------------------------------------------------
14388 
14389 const txDialog::Layout* txDialog::setLayout (const Layout* layout)
14390  {
14391 $1 assert (layout);
14392 
14393 $ return ::std::swap (layout_, layout), layout;
14394  }
14395 
14396 //-----------------------------------------------------------------------------------------------------------------
14397 
14398 intptr_t txDialog::dialogBox (WORD resourceID)
14399  {
14400 $1 const char* resName = (char*)(uintptr_t) resourceID;
14401 
14402 $ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0;
14403 
14404 $ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
14405  }
14406 
14407 //-----------------------------------------------------------------------------------------------------------------
14408 
14409 intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/)
14410  {
14411 $1 if (!layout) layout = layout_;
14412 $ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога), 0; $ if (!bufsize) bufsize = 1024; $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize); $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки), 0;
14413 
14414 $ if (!bufsize) bufsize = 1024;
14415 
14416 $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize);
14417 $ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога), 0; $ const Layout* dlg = &layout[0]; $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 }; $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize, (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0, dlg->x, dlg->y, dlg->sx, dlg->sy, dlg->caption? dlg->caption : def.caption, dlg->font? dlg->font : def.font, dlg->fontsize? dlg->fontsize : def.fontsize, NULL); $ WORD i = 0; $ for (i = 1; layout[i].wndclass != END; ++i) { $ const Layout* item = &layout[i]; $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl), item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy, item->id, (const char*)(uintptr_t) item->wndclass, item->caption); } $ tmpl->cdit = (unsigned short) (i-1); $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this); $ GlobalFree (tmpl); $ return res; } //----------------------------------------------------------------------------------------------------------------- int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM) { $1 switch (msg) { case WM_INITDIALOG: $ SetForegroundWindow (wnd); $ break; case WM_COMMAND: $ switch (LOWORD (wParam)) { case IDOK: case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow()); $ EndDialog (wnd, (uintptr_t) this); $ break; default: $ break; } $ break; default: $ break; } $ return FALSE; } //----------------------------------------------------------------------------------------------------------------- intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { $1 static txDialog* this__ = NULL; $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam; $ if (!this__) return FALSE; $ return this__-> dialogProc (wnd, msg, wParam, lParam); } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle, WORD controls, short x, short y, short cx, short cy, const char caption[], const char font[], WORD fontsize, const char menu[]) { $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL; $ WORD* pw = (WORD*) globalMem; $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->cdit = controls; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ if (menu > (const char*) 0xFFFF) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } else { $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0); $ *pw++ = (WORD)(uintptr_t) menu; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ if (style & DS_SETFONT) { $ *pw++ = fontsize; $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF)); } $ return pw; } //----------------------------------------------------------------------------------------------------------------- void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle, short x, short y, short cx, short cy, WORD id, const char wclass[], const char caption[]) { $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL; $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary $ ((ULONG&) pw) += 3; $ ((ULONG&) pw) >>= 2; $ ((ULONG&) pw) <<= 2; $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++; $ tmpl->style = style; $ tmpl->dwExtendedStyle = exStyle; $ tmpl->x = x; $ tmpl->y = y; $ tmpl->cx = cx; $ tmpl->cy = cy; $ tmpl->id = id; $ if (HIWORD (wclass) == 0xFFFF) { $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass)); $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass)); } else if (wclass) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ if (caption) { $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw, (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF)); } else { $ *pw++ = 0; } $ *pw++ = 0; $ return pw; } //} //================================================================================================================= //================================================================================================================= //{ Cleaning up the utility macros // Очистка служебных макросов //================================================================================================================= #undef $ #undef $0 #undef $1 #undef $2 #undef $3 #undef $4 #undef $5 #undef $6 #undef $7 #undef $8 #undef $9 #undef $$ //} //================================================================================================================= //! @endcond //================================================================================================================= //{ Experimental Debugging macros //! @name Экспериментальные отладочные макросы //================================================================================================================= //{---------------------------------------------------------------------------------------------------------------- //! @ingroup Misc //! @brief Отладочная печать переменной во время вычисления выражения или участка кода //! во время его выполнения. //! //! Сделай приятными твои <i>круглые сутки), 0;
14418 
14419 $ const Layout* dlg = &layout[0];
14420 $ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 };
14421 
14422 $ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize,
14423  (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0,
14424  dlg->x, dlg->y, dlg->sx, dlg->sy,
14425  dlg->caption? dlg->caption : def.caption,
14426  dlg->font? dlg->font : def.font,
14427  dlg->fontsize? dlg->fontsize : def.fontsize, NULL);
14428 $ WORD i = 0;
14429 $ for (i = 1; layout[i].wndclass != END; ++i)
14430  {
14431 $ const Layout* item = &layout[i];
14432 
14433 $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl),
14434  item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy,
14435  item->id, (const char*)(uintptr_t) item->wndclass, item->caption);
14436  }
14437 
14438 $ tmpl->cdit = (unsigned short) (i-1);
14439 
14440 $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
14441 
14442 $ GlobalFree (tmpl);
14443 
14444 $ return res;
14445  }
14446 
14447 //-----------------------------------------------------------------------------------------------------------------
14448 
14449 int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM)
14450  {
14451 $1 switch (msg)
14452  {
14453  case WM_INITDIALOG: $ SetForegroundWindow (wnd);
14454  $ break;
14455 
14456  case WM_COMMAND: $ switch (LOWORD (wParam))
14457  {
14458  case IDOK:
14459  case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow());
14460  $ EndDialog (wnd, (uintptr_t) this);
14461  $ break;
14462 
14463  default: $ break;
14464  }
14465  $ break;
14466  default: $ break;
14467  }
14468 
14469 $ return FALSE;
14470  }
14471 
14472 //-----------------------------------------------------------------------------------------------------------------
14473 
14474 intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
14475  {
14476 $1 static txDialog* this__ = NULL;
14477 $ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam;
14478 $ if (!this__) return FALSE;
14479 
14480 $ return this__-> dialogProc (wnd, msg, wParam, lParam);
14481  }
14482 
14483 //-----------------------------------------------------------------------------------------------------------------
14484 
14485 void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle,
14486  WORD controls, short x, short y, short cx, short cy,
14487  const char caption[], const char font[], WORD fontsize, const char menu[])
14488  {
14489 $1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL;
14490 
14491 $ WORD* pw = (WORD*) globalMem;
14492 
14493 $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++;
14494 
14495 $ tmpl->style = style;
14496 $ tmpl->dwExtendedStyle = exStyle;
14497 $ tmpl->cdit = controls;
14498 $ tmpl->x = x;
14499 $ tmpl->y = y;
14500 $ tmpl->cx = cx;
14501 $ tmpl->cy = cy;
14502 
14503 $ if (menu > (const char*) 0xFFFF)
14504  {
14505 $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw,
14506  (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
14507  }
14508  else
14509  {
14510 $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0);
14511 $ *pw++ = (WORD)(uintptr_t) menu;
14512  }
14513 
14514 $ if (caption)
14515  {
14516 $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw,
14517  (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
14518  }
14519 
14520 $ if (style & DS_SETFONT)
14521  {
14522 $ *pw++ = fontsize;
14523 $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw,
14524  (int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
14525  }
14526 
14527 $ return pw;
14528  }
14529 
14530 //-----------------------------------------------------------------------------------------------------------------
14531 
14532 void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle,
14533  short x, short y, short cx, short cy,
14534  WORD id, const char wclass[], const char caption[])
14535  {
14536 $1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL;
14537 
14538 $ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary
14539 $ ((ULONG&) pw) += 3;
14540 $ ((ULONG&) pw) >>= 2;
14541 $ ((ULONG&) pw) <<= 2;
14542 
14543 $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++;
14544 
14545 $ tmpl->style = style;
14546 $ tmpl->dwExtendedStyle = exStyle;
14547 $ tmpl->x = x;
14548 $ tmpl->y = y;
14549 $ tmpl->cx = cx;
14550 $ tmpl->cy = cy;
14551 $ tmpl->id = id;
14552 
14553 $ if (HIWORD (wclass) == 0xFFFF)
14554  {
14555 $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass));
14556 $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass));
14557  }
14558  else if (wclass)
14559  {
14560 $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw,
14561  (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
14562  }
14563  else
14564  {
14565 $ *pw++ = 0;
14566  }
14567 
14568 $ if (caption)
14569  {
14570 $ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw,
14571  (int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
14572  }
14573  else
14574  {
14575 $ *pw++ = 0;
14576  }
14577 
14578 $ *pw++ = 0;
14579 
14580 $ return pw;
14581  }
14582 
14583 //}
14584 //=================================================================================================================
14585 
14586 //=================================================================================================================
14587 //{ Cleaning up the utility macros
14588 // Очистка служебных макросов
14589 //=================================================================================================================
14590 
14591 #undef $
14592 #undef $0
14593 #undef $1
14594 #undef $2
14595 #undef $3
14596 #undef $4
14597 #undef $5
14598 #undef $6
14599 #undef $7
14600 #undef $8
14601 #undef $9
14602 #undef $$
14603 
14604 //}
14605 //=================================================================================================================
14606 
14608 
14609 //=================================================================================================================
14610 //{ Experimental Debugging macros
14612 //=================================================================================================================
14613 
14614 //{----------------------------------------------------------------------------------------------------------------
14734 //}----------------------------------------------------------------------------------------------------------------
14735 
14736 #ifndef __TX_DEBUG_MACROS
14737 #define __TX_DEBUG_MACROS ("Группа отладочных $-макросов")
14740 //-----------------------------------------------------------------------------------------------------------------
14741 
14742 #define $H txSetConsoleAttr (FOREGROUND_BLACK | BACKGROUND_BLACK);
14743 #define $B txSetConsoleAttr (FOREGROUND_BLUE | BACKGROUND_BLACK);
14744 #define $G txSetConsoleAttr (FOREGROUND_GREEN | BACKGROUND_BLACK);
14745 #define $C txSetConsoleAttr (FOREGROUND_CYAN | BACKGROUND_BLACK);
14746 #define $R txSetConsoleAttr (FOREGROUND_RED | BACKGROUND_BLACK);
14747 #define $M txSetConsoleAttr (FOREGROUND_MAGENTA | BACKGROUND_BLACK);
14748 #define $Y txSetConsoleAttr (FOREGROUND_DARKYELLOW | BACKGROUND_BLACK);
14749 #define $d txSetConsoleAttr (FOREGROUND_LIGHTGRAY | BACKGROUND_BLACK);
14750 #define $D txSetConsoleAttr (FOREGROUND_DARKGRAY | BACKGROUND_BLACK);
14751 #define $b txSetConsoleAttr (FOREGROUND_LIGHTBLUE | BACKGROUND_BLACK);
14752 #define $g txSetConsoleAttr (FOREGROUND_LIGHTGREEN | BACKGROUND_BLACK);
14753 #define $c txSetConsoleAttr (FOREGROUND_LIGHTCYAN | BACKGROUND_BLACK);
14754 #define $r txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK);
14755 #define $m txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA | BACKGROUND_BLACK);
14756 #define $y txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_BLACK);
14757 #define $h txSetConsoleAttr (FOREGROUND_WHITE | BACKGROUND_BLACK);
14758 
14759 #define $i txSetConsoleAttr (FOREGROUND_LIGHTCYAN | BACKGROUND_BLUE);
14760 #define $I txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_BLUE);
14761 #define $o txSetConsoleAttr (FOREGROUND_LIGHTGREEN | BACKGROUND_GREEN);
14762 #define $O txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_GREEN);
14763 #define $e txSetConsoleAttr (FOREGROUND_WHITE | BACKGROUND_RED);
14764 #define $E txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_RED);
14765 #define $w txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA | BACKGROUND_MAGENTA);
14766 #define $W txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_MAGENTA);
14767 #define $f txSetConsoleAttr (FOREGROUND_BLACK | BACKGROUND_LIGHTRED);
14768 #define $F txSetConsoleAttr (FOREGROUND_MAGENTA | BACKGROUND_LIGHTRED);
14769 #define $l txSetConsoleAttr (FOREGROUND_BLACK | BACKGROUND_DARKGRAY);
14770 #define $L txSetConsoleAttr (FOREGROUND_LIGHTGRAY | BACKGROUND_DARKGRAY);
14771 
14772 #define $T( cond ) txSetConsoleAttr ((cond)? FOREGROUND_LIGHTGREEN : FOREGROUND_LIGHTRED );
14773 
14774 #define $s _txSaveConsoleAttr TX_JOIN (__txSavedConsoleAttrs, __LINE__);
14775 
14776 #define $sH $s $H
14777 #define $sB $s $B
14778 #define $sG $s $G
14779 #define $sC $s $C
14780 #define $sR $s $R
14781 #define $sM $s $M
14782 #define $sY $s $Y
14783 #define $sd $s $d
14784 #define $sD $s $D
14785 #define $sb $s $b
14786 #define $sg $s $g
14787 #define $sc $s $c
14788 #define $sr $s $r
14789 #define $sm $s $m
14790 #define $sy $s $y
14791 #define $sh $s $h
14792 
14793 #define $si $s $i
14794 #define $sI $s $I
14795 #define $so $s $o
14796 #define $sO $s $O
14797 #define $se $s $e
14798 #define $sE $s $E
14799 #define $sw $s $w
14800 #define $sW $s $W
14801 #define $sf $s $f
14802 #define $sF $s $F
14803 #define $sl $s $l
14804 #define $sL $s $L
14805 
14806 #define $sT( cond ) $s $T (cond)
14807 
14808 #define $test(cond) { if (!!(cond)) { $o std::cerr << "[PASSED] " __TX_FILELINE__ ": " #cond; } \
14809  else { $e std::cerr << "[FAILED] " __TX_FILELINE__ ": " #cond; } $d; }
14810 
14811 #define $status(cond) $test (cond)
14812 
14813 #define $unittest( code, expected ) \
14814  { \
14815  const _tx_decltype (code) & _result = (code); /* Should use auto, but g++ 4.7.2 default std is < 2011 */ \
14816  const _tx_decltype (expected) & _expected = (expected); \
14817  \
14818  if (_result == _expected) \
14819  { $so std::cerr << "[PASSED] " __TX_FILELINE__ ": " #code; } \
14820  else \
14821  { $se std::cerr << "[FAILED] " __TX_FILELINE__ ": " #code " == (" << _result << "), should be (" << _expected << ")"; } \
14822  \
14823  $n; \
14824  (_result == _expected); \
14825  }
14826 
14827 //=================================================================================================================
14828 
14829 #ifdef _GCC_VER
14830 
14831 #define $V( var, ...) ( _txDumpVar ((var), "[", __VA_ARGS__ "]\n") )
14832 #define $V_( var, ...) ( _txDumpVar ((var), "[", __VA_ARGS__ "] " ) )
14833 #define $V__(var, ...) ( _txDumpVar ((var), "[", __VA_ARGS__ "]" ) )
14834 
14835 #define $( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]\n") )
14836 #define $_( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "] " ) )
14837 #define $__( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]" ) )
14838 
14839 #define $x( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]\n", ::std::ios_base::showbase | ::std::ios_base::hex) )
14840 #define $x_( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "] ", ::std::ios_base::showbase | ::std::ios_base::hex) )
14841 
14842 #define $v( var, cond, ...) { { $st (cond); _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]" ); } $n; }
14843 #define $v_( var, cond, ...) { $st (cond); _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]" ); }
14844 
14845 #else
14846 
14847 #define $V( var, ...) ( _txDumpVar ((var), "[", #__VA_ARGS__ "]\n") )
14848 #define $V_( var, ...) ( _txDumpVar ((var), "[", #__VA_ARGS__ "] " ) )
14849 #define $V__(var, ...) ( _txDumpVar ((var), "[", #__VA_ARGS__ "]" ) )
14850 
14851 #define $( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]\n") )
14852 #define $_( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "] " ) )
14853 #define $__( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]" ) )
14854 
14855 #define $x( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]\n", ::std::ios_base::showbase | ::std::ios_base::hex) )
14856 #define $x_( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "] ", ::std::ios_base::showbase | ::std::ios_base::hex) )
14857 
14858 #define $v( var, cond, ...) { { $st (cond); _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]" ); } $n; }
14859 #define $v_( var, cond, ...) { $st (cond); _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]" ); }
14860 
14861 #endif
14862 
14863 #define $$ { txOutputDebugPrintf ("\f\n"); { $sC txOutputDebugPrintf ("\f" "[%s (%d) %s]", __FILE__, __LINE__, __TX_FUNCTION__); } txOutputDebugPrintf ("\f\n"); }
14864 #define $$_ { txOutputDebugPrintf ("\f\n"); { $sC txOutputDebugPrintf ("\f" "[" "(%d) %s]", __LINE__, __func__); } txOutputDebugPrintf ("\f\n"); }
14865 #define $meow(...) { txOutputDebugPrintf ("\f\n"); { $sc txOutputDebugPrintf ("\f" "[%s (%d) %s]", __FILE__, __LINE__, __func__); txOutputDebugPrintf ("\f " __VA_ARGS__); } txOutputDebugPrintf ("\f\n"); }
14866 #define $meow_ { txOutputDebugPrintf ("\f\n"); { $sc txOutputDebugPrintf ("\f" "[%s (%d) %s]", __FILE__, __LINE__, __func__); txOutputDebugPrintf ("\f"); } txOutputDebugPrintf ("\f\n"); }
14867 
14868 #define $$$( ... ) ( ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n", _txDumpVar ((__VA_ARGS__),"\n[" __TX_FILELINE__ ": " #__VA_ARGS__ ": ", ", DONE]\n\n") )
14869 #define $$$_( ... ) ( ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n", _txDumpVar ((__VA_ARGS__), "[" __TX_FILELINE__ ": " #__VA_ARGS__ ": ", ", DONE]\n\n") )
14870 
14871 #define $$$$( ... ) { ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n"; _txDumpVarSuffix ("\n[" __TX_FILELINE__ ": " #__VA_ARGS__ " DONE]\n\n"); { __VA_ARGS__; } }
14872 #define $$$$_( ... ) { ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n"; _txDumpVarSuffix ( "[" __TX_FILELINE__ ": " #__VA_ARGS__ " DONE]\n\n"); { __VA_ARGS__; } }
14873 #define $do( ... ) ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n"; __VA_ARGS__
14874 #define $DO( ... ) ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]...\n"; $p; __VA_ARGS__
14875 #define $Do( ... ) ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]...\n"; \
14876  txMessageBox ( "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]...\n", __TX_FUNCTION__); __VA_ARGS__
14877 
14878 #define $n { ::std::cerr << "\n"; }
14879 #define $nn { ::std::cerr << "\n\n"; }
14880 #define $t { ::std::cerr << "\t"; }
14881 
14882 //-----------------------------------------------------------------------------------------------------------------
14883 
14884 // This will never be documented, he-he. Read the source, Luke.
14885 
14886 #if defined (_DEBUG)
14887  #define $debug if (1)
14888  #define $printf if (1)
14889  #define $PRINTF if (1)
14890 #else
14891  #define $debug if (0)
14892  #define $printf if (0)
14893  #define $PRINTF if (0)
14894 #endif
14895 
14896 #define $$d $debug
14897 #define $$w $$$$
14898 #define $$s __TX_FILELINE__
14899 #define $$b DebugBreak()
14900 #define $$p { if (txMessageBox (__TX_FILELINE__ "\n\n" "[Повтор] - продолжение программы,\n" "[Отмена] - остановка", \
14901  __TX_FUNCTION__, MB_ICONINFORMATION | MB_RETRYCANCEL) == IDCANCEL) ::exit (2); }
14902 #define $$P ( txMessageBox (__TX_FILELINE__, __TX_FUNCTION__, MB_ICONINFORMATION | MB_YESNOCANCEL) )
14903 #define $ppp { { $sy; fprintf (stderr, "[%s ""%s: ""Нажмите любую клавишу для продолжения]", __TX_FILELINE__, __TX_FUNCTION__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14904 #define $pp { { $sy; fprintf (stderr, "[%04d %s: ""Нажмите любую клавишу для продолжения]", __LINE__, __TX_FUNCTION__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14905 #define $p { { $sy; fprintf (stderr, "[%s ""%s(): Нажмите любую клавишу для продолжения]", __TX_FILELINE__, __func__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14906 #define $ppp_ { { $sy; fprintf (stderr, "[%s ""%s]", __TX_FILELINE__, __TX_FUNCTION__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14907 #define $pp_ { { $sy; fprintf (stderr, "[%04d %s]", __LINE__, __TX_FUNCTION__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14908 #define $p_ { { $sy; fprintf (stderr, "[%s ""%s()]", __TX_FILELINE__, __func__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14909 #define $P ( ((int(*)()) (_getch)) () ) // Avoid "return value not used"
14910 
14911 //-----------------------------------------------------------------------------------------------------------------
14912 
14913 struct _txSaveConsoleAttr
14914  {
14915  unsigned attr_;
14916 
14917  inline _txSaveConsoleAttr() : attr_ (txGetConsoleAttr ()) {}
14918  inline explicit _txSaveConsoleAttr (WORD attr) : attr_ (txGetConsoleAttr ()) { txSetConsoleAttr (attr); }
14919  inline ~_txSaveConsoleAttr() { txSetConsoleAttr (attr_); }
14920  };
14921 
14922 //-----------------------------------------------------------------------------------------------------------------
14923 
14924 struct _txDumpVarSuffix
14925  {
14926  typedef _txDumpVarSuffix this_t;
14927 
14928  const char* suffix_;
14929 
14930  inline explicit _txDumpVarSuffix (const char suffix[] = "") : suffix_ (suffix) { assert (suffix); }
14931  inline ~_txDumpVarSuffix() { ::std::cerr << suffix_; }
14932 
14933  _txDumpVarSuffix (const this_t&) _tx_delete;
14934  this_t& operator = (const this_t&) _tx_delete;
14935  };
14936 
14937 //-----------------------------------------------------------------------------------------------------------------
14938 
14939 #define ARGS__ const char* prefix, const char* suffix, std::ios_base::fmtflags flags, int deep
14940 #define ARGS_ const char* prefix, const char* suffix, std::ios_base::fmtflags flags = std::ios_base::fmtflags(), int deep = 0
14941 #define VALS_ prefix, suffix, flags, deep
14942 
14943 template <typename T, typename StreamT> const T& _txDumpVal (const T& value, StreamT& stream, ARGS_);
14944 
14945 //-----------------------------------------------------------------------------------------------------------------
14946 
14947 template <typename T> inline const T& _txDumpVar (const T& value, ARGS_) { _txDumpVal (value, std:: cerr, VALS_); return value; }
14948 template <typename T> inline T& _txDumpVar ( T& value, ARGS_) { _txDumpVal (value, std:: cerr, VALS_); return value; }
14949 
14950 template <int N> inline const char (&_txDumpVar (const char (&value) [N], ARGS_)) [N] { _txDumpVal (value, std:: cerr, VALS_); return value; }
14951 template <int N> inline char (&_txDumpVar ( char (&value) [N], ARGS_)) [N] { _txDumpVal (value, std:: cerr, VALS_); return value; }
14952 
14953 template <int N> inline const wchar_t (&_txDumpVar (const wchar_t (&value) [N], ARGS_)) [N] { _txDumpVal (value, std::wcerr, VALS_); return value; }
14954 template <int N> inline wchar_t (&_txDumpVar ( wchar_t (&value) [N], ARGS_)) [N] { _txDumpVal (value, std::wcerr, VALS_); return value; }
14955 
14956  inline const wchar_t& _txDumpVar (const wchar_t& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_); return value; }
14957  inline wchar_t& _txDumpVar ( wchar_t& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_); return value; }
14958 
14959  inline const wchar_t*& _txDumpVar (const wchar_t*& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_); return value; }
14960  inline wchar_t*& _txDumpVar ( wchar_t*& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_); return value; }
14961 
14962  inline const std::wstring& _txDumpVar (const std::wstring& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_); return value; }
14963  inline std::wstring& _txDumpVar ( std::wstring& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_); return value; }
14964 
14965 //-----------------------------------------------------------------------------------------------------------------
14966 
14967 template <typename T, typename StreamT> inline void _txDumpVal (const T& value, StreamT& stream) { stream << value; }
14968 template <typename StreamT> inline void _txDumpVal (const char value, StreamT& stream) { stream << "'" << value << "'"; }
14969 template <typename StreamT> inline void _txDumpVal (const wchar_t value, StreamT& stream) { stream << L"'" << value << L"'"; }
14970 template <typename StreamT> inline void _txDumpVal (const char* value, StreamT& stream) { if (value) stream << '"' << value << '"'; else stream << "(null)"; }
14971 template <typename StreamT> inline void _txDumpVal (const wchar_t* value, StreamT& stream) { if (value) stream << L'"' << value << L'"'; else stream << L"(null)"; }
14972 template <typename StreamT> inline void _txDumpVal (const std::string& value, StreamT& stream) { if (value.length()) stream << '"' << value << '"'; else stream << "(empty)"; }
14973 template <typename StreamT> inline void _txDumpVal (const std::wstring& value, StreamT& stream) { if (value.length()) stream << L'"' << value << L'"'; else stream << L"(empty)"; }
14974 
14975 //-----------------------------------------------------------------------------------------------------------------
14976 
14977 template <typename T, typename StreamT>
14978 inline const T& _txDumpVal (const T& value, StreamT& stream, ARGS__)
14979  {
14980  if (_TX_ARGUMENT_FAILED (&value)) return value;
14981  if (_TX_ARGUMENT_FAILED (&stream)) return value;
14982  if (_TX_ARGUMENT_FAILED ( prefix)) return value;
14983  if (_TX_ARGUMENT_FAILED ( suffix)) return value;
14984 
14985  $sc;
14986  if (!deep) stream << prefix;
14987 
14988  std::ios_base::fmtflags old = stream.flags ((flags)? flags : stream.flags());
14989 
14990  if (!_txIsBadReadPtr (&value))
14991  {
14992  _txDumpVal (value, stream);
14993  }
14994  else
14995  {
14996  $sE; stream << "<НЕВЕРНЫЙ АДРЕС " << &value << ">";
14997  }
14998 
14999  stream.flags (old);
15000 
15001  if (!deep) stream << ((*suffix == ']')? "" : ": ") << suffix;
15002 
15003  return value;
15004  }
15005 
15006 //-----------------------------------------------------------------------------------------------------------------
15007 
15008 template <typename T, int N>
15009 inline T (&_txDumpVar (T (&value) [N], ARGS_)) [N]
15010  {
15011  if (_TX_ARGUMENT_FAILED (&value)) return value;
15012  if (_TX_ARGUMENT_FAILED ( prefix)) return value;
15013  if (_TX_ARGUMENT_FAILED ( suffix)) return value;
15014 
15015  std::ostream& stream = std::cerr;
15016 
15017  $sc; if (!deep) std::cerr << prefix;
15018  $C; std::cerr << ((deep)? " {" : "{");
15019 
15020  if (!_txIsBadReadPtr (value))
15021  {
15022  for (int i = 0; ; i++)
15023  {
15024  { $sC; stream << "[" << i << "]="; }
15025 
15026  _txDumpVar (value[i], prefix, suffix, flags, deep+1);
15027 
15028  if (i >= N-1) break;
15029 
15030  stream << ", ";
15031  }
15032  }
15033  else
15034  {
15035  $sE; stream << "<НЕВЕРНЫЙ АДРЕС " << &value << ">";
15036  }
15037 
15038  $C; std::cerr << "}";
15039  $c; if (!deep) std::cerr << ((*suffix == ']')? "" : ": ") << suffix;
15040 
15041  return value;
15042  }
15043 
15044 #undef ARGS__
15045 #undef ARGS_
15046 #undef VALS_
15047 
15048 //=================================================================================================================
15049 
15050 inline std::ostream& operator << (std::ostream& stream, const POINT& point)
15051  {
15052  if (_TX_ARGUMENT_FAILED (&stream)) return stream;
15053  if (_TX_ARGUMENT_FAILED (&point)) return stream;
15054 
15055  if (&point) stream << "{ x: " << point.x << ", y: " << point.y << " }";
15056  else stream << "(null)";
15057 
15058  return stream;
15059  }
15060 
15061 inline std::ostream& operator << (std::ostream& stream, const SIZE& size)
15062  {
15063  if (_TX_ARGUMENT_FAILED (&stream)) return stream;
15064  if (_TX_ARGUMENT_FAILED (&size)) return stream;
15065 
15066  if (&size) stream << "{ cx: " << size.cx << ", cy: " << size.cy << " }";
15067  else stream << "(null)";
15068 
15069  return stream;
15070  }
15071 
15072 inline std::ostream& operator << (std::ostream& stream, const RECT& rect)
15073  {
15074  if (_TX_ARGUMENT_FAILED (&stream)) return stream;
15075  if (_TX_ARGUMENT_FAILED (&rect)) return stream;
15076 
15077  if (&rect) stream << "{ left: " << rect.left << ", top: " << rect.top <<
15078  ", right: " << rect.right << ", bottom: " << rect.bottom << " }";
15079 
15080  else stream << "(null)";
15081 
15082  return stream;
15083  }
15084 
15085 //-----------------------------------------------------------------------------------------------------------------
15087 
15088 #endif
15089 
15090 //}
15091 //=================================================================================================================
15092 
15094 
15095 //=================================================================================================================
15096 //{ TXAPI calls tracing
15097 // Трассировка вызовов TXAPI
15098 //=================================================================================================================
15099 
15100 #ifndef FOR_DOXYGEN_ONLY
15101 
15102 #if defined (_MSC_VER)
15103 #undef _txLocCurSet
15104 #define _txLocCurSet() __txLocCurSet (__FILE__, __LINE__, NULL)
15105 #endif
15106 
15107 #define txAlphaBlend(...) ( _txLocCurSet(), txAlphaBlend (__VA_ARGS__) )
15108 #define txArc(...) ( _txLocCurSet(), txArc (__VA_ARGS__) )
15109 #define txBegin(...) ( _txLocCurSet(), txBegin (__VA_ARGS__) )
15110 #define txBitBlt(...) ( _txLocCurSet(), txBitBlt (__VA_ARGS__) )
15111 #define txChord(...) ( _txLocCurSet(), txChord (__VA_ARGS__) )
15112 #define txCircle(...) ( _txLocCurSet(), txCircle (__VA_ARGS__) )
15113 #define txClear(...) ( _txLocCurSet(), txClear (__VA_ARGS__) )
15114 #define txClearConsole(...) ( _txLocCurSet(), txClearConsole (__VA_ARGS__) )
15115 #define txColor(...) ( _txLocCurSet(), txColor (__VA_ARGS__) )
15116 #define txCreateCompatibleDC(...) ( _txLocCurSet(), txCreateCompatibleDC (__VA_ARGS__) )
15117 #define txCreateDIBSection(...) ( _txLocCurSet(), txCreateDIBSection (__VA_ARGS__) )
15118 #define txCreateExtraWindow(...) ( _txLocCurSet(), txCreateExtraWindow (__VA_ARGS__) )
15119 #define txCreateExtraWindow(...) ( _txLocCurSet(), txCreateExtraWindow (__VA_ARGS__) )
15120 #define txCreateWindow(...) ( _txLocCurSet(), txCreateWindow (__VA_ARGS__) )
15121 #define txDC(...) ( _txLocCurSet(), txDC (__VA_ARGS__) )
15122 #define txDeleteDC(...) ( _txLocCurSet(), txDeleteDC (__VA_ARGS__) )
15123 #define txDemangle(...) ( _txLocCurSet(), txDemangle (__VA_ARGS__) )
15124 #define txDestroyWindow(...) ( _txLocCurSet(), txDestroyWindow (__VA_ARGS__) )
15125 #define txDisableAutoPause(...) ( _txLocCurSet(), txDisableAutoPause (__VA_ARGS__) )
15126 #define txDrawText(...) ( _txLocCurSet(), txDrawText (__VA_ARGS__) )
15127 #define txEllipse(...) ( _txLocCurSet(), txEllipse (__VA_ARGS__) )
15128 #define txEnd(...) ( _txLocCurSet(), txEnd (__VA_ARGS__) )
15129 #define txExtractColor(...) ( _txLocCurSet(), txExtractColor (__VA_ARGS__) )
15130 #define txFillColor(...) ( _txLocCurSet(), txFillColor (__VA_ARGS__) )
15131 #define txFloodFill(...) ( _txLocCurSet(), txFloodFill (__VA_ARGS__) )
15132 #define txFontExist(...) ( _txLocCurSet(), txFontExist (__VA_ARGS__) )
15133 #define txFormat(...) ( _txLocCurSet(), txFormat (__VA_ARGS__) )
15134 #define txGetAsyncKeyState(...) ( _txLocCurSet(), txGetAsyncKeyState (__VA_ARGS__) )
15135 #define txGetColor(...) ( _txLocCurSet(), txGetColor (__VA_ARGS__) )
15136 #define txGetConsoleAttr(...) ( _txLocCurSet(), txGetConsoleAttr (__VA_ARGS__) )
15137 #define txGetConsoleCursorPos(...) ( _txLocCurSet(), txGetConsoleCursorPos (__VA_ARGS__) )
15138 #define txGetConsoleExtent(...) ( _txLocCurSet(), txGetConsoleExtent (__VA_ARGS__) )
15139 #define txGetConsoleFontSize(...) ( _txLocCurSet(), txGetConsoleFontSize (__VA_ARGS__) )
15140 #define txGetExtent(...) ( _txLocCurSet(), txGetExtent (__VA_ARGS__) )
15141 #define txGetExtentX(...) ( _txLocCurSet(), txGetExtentX (__VA_ARGS__) )
15142 #define txGetExtentY(...) ( _txLocCurSet(), txGetExtentY (__VA_ARGS__) )
15143 #define txGetFillColor(...) ( _txLocCurSet(), txGetFillColor (__VA_ARGS__) )
15144 #define txGetFPS(...) ( _txLocCurSet(), txGetFPS (__VA_ARGS__) )
15145 #define txGetModuleFileName(...) ( _txLocCurSet(), txGetModuleFileName (__VA_ARGS__) )
15146 #define txGetPixel(...) ( _txLocCurSet(), txGetPixel (__VA_ARGS__) )
15147 #define txGetTextExtent(...) ( _txLocCurSet(), txGetTextExtent (__VA_ARGS__) )
15148 #define txGetTextExtentX(...) ( _txLocCurSet(), txGetTextExtentX (__VA_ARGS__) )
15149 #define txGetTextExtentY(...) ( _txLocCurSet(), txGetTextExtentY (__VA_ARGS__) )
15150 #define txHSL2RGB(...) ( _txLocCurSet(), txHSL2RGB (__VA_ARGS__) )
15151 #define txInputBox(...) ( _txLocCurSet(), txInputBox (__VA_ARGS__) )
15152 #define txLine(...) ( _txLocCurSet(), txLine (__VA_ARGS__) )
15153 #define txLoadImage(...) ( _txLocCurSet(), txLoadImage (__VA_ARGS__) )
15154 #define txLock(...) ( _txLocCurSet(), txLock (__VA_ARGS__) )
15155 #define txMessageBox(...) ( _txLocCurSet(), txMessageBox (__VA_ARGS__) )
15156 #define txMouseButtons(...) ( _txLocCurSet(), txMouseButtons (__VA_ARGS__) )
15157 #define txMousePos(...) ( _txLocCurSet(), txMousePos (__VA_ARGS__) )
15158 #define txMouseX(...) ( _txLocCurSet(), txMouseX (__VA_ARGS__) )
15159 #define txMouseY(...) ( _txLocCurSet(), txMouseY (__VA_ARGS__) )
15160 #define txNotifyIcon(...) ( _txLocCurSet(), txNotifyIcon (__VA_ARGS__) )
15161 #define txOK(...) ( _txLocCurSet(), txOK (__VA_ARGS__) )
15162 #define txOutputDebugPrintf(...) ( _txLocCurSet(), txOutputDebugPrintf (__VA_ARGS__) )
15163 #define txPie(...) ( _txLocCurSet(), txPie (__VA_ARGS__) )
15164 #define txPixel(...) ( _txLocCurSet(), txPixel (__VA_ARGS__) )
15165 #define txPlaySound(...) ( _txLocCurSet(), txPlaySound (__VA_ARGS__) )
15166 #define txPlayVideo(...) ( _txLocCurSet(), txPlayVideo (__VA_ARGS__) )
15167 #define txPolygon(...) ( _txLocCurSet(), txPolygon (__VA_ARGS__) )
15168 #define txPrintf(...) ( _txLocCurSet(), txPrintf (__VA_ARGS__) )
15169 #define txQueryPerformance(...) ( _txLocCurSet(), txQueryPerformance (__VA_ARGS__) )
15170 #define txRectangle(...) ( _txLocCurSet(), txRectangle (__VA_ARGS__) )
15171 #define txRedrawWindow(...) ( _txLocCurSet(), txRedrawWindow (__VA_ARGS__) )
15172 #define txRegisterClass(...) ( _txLocCurSet(), txRegisterClass (__VA_ARGS__) )
15173 #define txRegisterClass(...) ( _txLocCurSet(), txRegisterClass (__VA_ARGS__) )
15174 #define txRegQuery(...) ( _txLocCurSet(), txRegQuery (__VA_ARGS__) )
15175 #define txReopenStdio(...) ( _txLocCurSet(), txReopenStdio (__VA_ARGS__) )
15176 #define txReopenStdio(...) ( _txLocCurSet(), txReopenStdio (__VA_ARGS__) )
15177 #define txRGB2HSL(...) ( _txLocCurSet(), txRGB2HSL (__VA_ARGS__) )
15178 #define txSaveImage(...) ( _txLocCurSet(), txSaveImage (__VA_ARGS__) )
15179 #define txSelectFont(...) ( _txLocCurSet(), txSelectFont (__VA_ARGS__) )
15180 #define txSelectObject(...) ( _txLocCurSet(), txSelectObject (__VA_ARGS__) )
15181 #define txSetColor(...) ( _txLocCurSet(), txSetColor (__VA_ARGS__) )
15182 #define txSetConsoleAttr(...) ( _txLocCurSet(), txSetConsoleAttr (__VA_ARGS__) )
15183 #define txSetConsoleCursorPos(...) ( _txLocCurSet(), txSetConsoleCursorPos (__VA_ARGS__) )
15184 #define txSetDefaults(...) ( _txLocCurSet(), txSetDefaults (__VA_ARGS__) )
15185 #define txSetFillColor(...) ( _txLocCurSet(), txSetFillColor (__VA_ARGS__) )
15186 #define txSetLocale(...) ( _txLocCurSet(), txSetLocale (__VA_ARGS__) )
15187 #define txSetPixel(...) ( _txLocCurSet(), txSetPixel (__VA_ARGS__) )
15188 #define txSetTextAlign(...) ( _txLocCurSet(), txSetTextAlign (__VA_ARGS__) )
15189 #define txSetWindowsHook(...) ( _txLocCurSet(), txSetWindowsHook (__VA_ARGS__) )
15190 #define txSleep(...) ( _txLocCurSet(), txSleep (__VA_ARGS__) )
15191 #define txSpeak(...) ( _txLocCurSet(), txSpeak (__VA_ARGS__) )
15192 #define txTaskKill(...) ( _txLocCurSet(), txTaskKill (__VA_ARGS__) )
15193 #define txTextCursor(...) ( _txLocCurSet(), txTextCursor (__VA_ARGS__) )
15194 #define txTextOut(...) ( _txLocCurSet(), txTextOut (__VA_ARGS__) )
15195 #define txTransparentBlt(...) ( _txLocCurSet(), txTransparentBlt (__VA_ARGS__) )
15196 #define txTriangle(...) ( _txLocCurSet(), txTriangle (__VA_ARGS__) )
15197 #define txUnlock(...) ( _txLocCurSet(), txUnlock (__VA_ARGS__) )
15198 #define txUpdateWindow(...) ( _txLocCurSet(), txUpdateWindow (__VA_ARGS__) )
15199 #define txUseAlpha(...) ( _txLocCurSet(), txUseAlpha (__VA_ARGS__) )
15200 #define txVersion(...) ( _txLocCurSet(), txVersion (__VA_ARGS__) )
15201 #define txVersionNumber(...) ( _txLocCurSet(), txVersionNumber (__VA_ARGS__) )
15202 #define txWindow(...) ( _txLocCurSet(), txWindow (__VA_ARGS__) )
15203 #define tx_fpreset(...) ( _txLocCurSet(), tx_fpreset (__VA_ARGS__) )
15204 #define tx_glGetError(...) ( _txLocCurSet(), tx_glGetError (__VA_ARGS__) )
15205 #define _txDump(...) ( _txLocCurSet(), _txDump (__VA_ARGS__) )
15206 #define _txStackBackTrace(...) ( _txLocCurSet(), _txStackBackTrace (__VA_ARGS__) )
15207 
15208 #endif
15209 
15210 //}
15211 //=================================================================================================================
15212 
15214 //}
15215 //=================================================================================================================
15216 
15219 } } // namespace TX, anonymous namespace
15220 
15223 //-----------------------------------------------------------------------------------------------------------------
15224 //{ The namespaces: easy using of TX:: and some of std::
15225 //-----------------------------------------------------------------------------------------------------------------
15226 
15227 using namespace TX; // Allow easy usage of TXLib functions
15228 
15229 using ::std::cin; // Predefined usings to avoid "using namespace std"
15230 using ::std::cout;
15231 using ::std::cerr;
15232 using ::std::string;
15233 using ::std::wcin;
15234 using ::std::wcout;
15235 using ::std::wcerr;
15236 using ::std::wstring;
15237 
15238 //}
15239 //-----------------------------------------------------------------------------------------------------------------
15240 
15241 //-----------------------------------------------------------------------------------------------------------------
15242 //{ Compiler- and platform-specific
15243 // Адаптация к компиляторам и платформам
15244 //-----------------------------------------------------------------------------------------------------------------
15246 
15247 #if defined (_GCC_VER)
15248 
15249  #pragma GCC optimize "strict-aliasing"
15250 
15251  #pragma GCC pop_options
15252  #pragma GCC diagnostic pop
15253 
15254  #endif
15255 
15256 #if defined (_CLANG_VER)
15257 
15258  #pragma clang diagnostic pop
15259 
15260  #endif
15261 
15262 //-----------------------------------------------------------------------------------------------------------------
15263 
15264 #if defined (_MSC_VER)
15265 
15266  #pragma warning (pop) // Restoring maximum level
15267 
15268  #endif
15269 
15270 #if defined (__INTEL_COMPILER)
15271 
15272  #pragma warning (default: 174) // Remark: expression has no effect
15273  #pragma warning (default: 304) // Remark: access control not specified ("public" by default)
15274  #pragma warning (default: 444) // Remark: destructor for base class "..." is not virtual
15275  #pragma warning (default: 522) // Remark: function redeclared "inline" after being called
15276  #pragma warning (default: 1684) // Conversion from pointer to same-sized integral type (potential portability problem)
15277 
15278  #pragma warning (disable: 981) // Remark: operands are evaluated in unspecified order
15279 
15280  #endif
15281 
15283 //}
15284 //-----------------------------------------------------------------------------------------------------------------
15285 
15286 #endif // __TXLIB_H_INCLUDED
15287 
15288 //=================================================================================================================
15289 // EOF
15290 //=================================================================================================================
15291 
15292 
15293 
15294 
15295 
15296 
15297 
15298 
15299 
txUpdateWindow
int txUpdateWindow(int update=true)
Разрешает или запрещает автоматическое обновление изображения в окне.
TX_LIGHTMAGENTA
const COLORREF TX_LIGHTMAGENTA
Светло-малиновый цвет. Еще менее лучшего оттенка.
Definition: TXLib.h:1423
txDialog::END
@ END
Конец описания диалога
Definition: TXLib.h:6080
txOK
bool txOK() tx_nodiscard
Проверка правильности работы библиотеки
txSpeak
int txSpeak(const char *text,...) tx_printfy(1)
Читает @strike мысли @endstrike текст вслух.
txAutoLock::txAutoLock
txAutoLock(CRITICAL_SECTION *cs, bool mandatory=true)
Конструктор, блокирует критическую секцию
Definition: TXLib.h:5950
_txWindowStyle
int _txWindowStyle
Стиль графического окна библиотеки.
Definition: TXLib.h:5438
txGetFillColor
COLORREF txGetFillColor(HDC dc=txDC()) tx_nodiscard
Возвращает текущий цвет заполнения фигур.
ID_INPUT_
#define ID_INPUT_
txAutoLock::~txAutoLock
~txAutoLock()
Деструктор, разблокирует секцию
Definition: TXLib.h:5984
__TX_FUNCTION__
#define __TX_FUNCTION__
Имя текущей функции
Definition: TXLib.h:271
TX_DEBUG_ERROR
#define TX_DEBUG_ERROR(...)
Выводит развернутое диагностическое сообщение в отладочном режиме.
Definition: TXLib.h:4965
txGetExtentY
int txGetExtentY(HDC dc=txDC()) tx_nodiscard
Возвращает высоту окна или холста.
txSelectObject
bool txSelectObject(HGDIOBJ obj, HDC dc=txDC())
Устанавливает текущий активный объект GDI.
tx_nodiscard
#define tx_nodiscard
Definition: TXLib.h:988
txGetAsyncKeyState
bool txGetAsyncKeyState(int key)
Проверяет, нажата ли указанная клавиша.
TX_YELLOW
const COLORREF TX_YELLOW
Желтый цвет.
Definition: TXLib.h:1425
txTriangle
bool txTriangle(double x1, double y1, double x2, double y2, double x3, double y3)
Функция, которая должна бы рисовать треугольник.
Definition: TXLib.h:2038
txSqr
double txSqr(double x)
Очень удобное возведение числа в квадрат.
Definition: TXLib.h:4600
TX_NULL
const COLORREF TX_NULL
Прозрачный цвет. Отключает рисование.
Definition: TXLib.h:1428
_txWindowUpdateInterval
unsigned _txWindowUpdateInterval
Интервал обновления холста (мс)
Definition: TXLib.h:5461
txLoadImage
HDC txLoadImage(const char filename[], unsigned imageFlags=IMAGE_BITMAP, unsigned loadFlags=LR_LOADFROMFILE) tx_nodiscard
Загружает из файла изображение в формате BMP. Делает это довольно медленно.
_tx_auto_func_::func_
T func_
Definition: TXLib.h:4753
txSetTextAlign
unsigned txSetTextAlign(unsigned align=TA_CENTER|TA_BASELINE, HDC dc=txDC())
Устанавливает текущее выравнивание текста (влево/вправо/по центру).
txGetExtentX
int txGetExtentX(HDC dc=txDC()) tx_nodiscard
Возвращает ширину окна или холста.
_tx_auto_func_::this_t
_tx_auto_func_< T > this_t
Definition: TXLib.h:4752
_txConsoleFont
const char * _txConsoleFont
Шрифт консоли
Definition: TXLib.h:5446
TX_BROWN
const COLORREF TX_BROWN
Коричневый цвет. Некрасивый. Do it yourself with RGB().
Definition: TXLib.h:1414
txDialog::Layout::caption
const char * caption
Название или текст
Definition: TXLib.h:6106
_txConsoleMode
int _txConsoleMode
Режим отображения консольного окна. Допустимы любые флаги функции ShowWindow.
Definition: TXLib.h:5421
txDialog::txDialog
txDialog()
Конструктор.
_TX_CODEPAGE
const int _TX_CODEPAGE
Смена кодовой страницы консоли и локали стандартной библиотеки С++.
Definition: TXLib.h:5320
PRIu64
#define PRIu64
Definition: TXLib.h:1006
txDialog::dialogProc
virtual int dialogProc(HWND _wnd, UINT _msg, WPARAM _wParam, LPARAM _lParam)
Функция обработки сообщений диалогового окна.
txPolygon
bool txPolygon(const POINT points[], int numPoints, HDC dc=txDC())
Рисует ломаную линию или многоугольник.
txCreateWindow
HWND txCreateWindow(double sizeX, double sizeY, bool centered=true)
Создание окна рисования
txLine
bool txLine(double x0, double y0, double x1, double y1, HDC dc=txDC())
Рисует линию.
fopen_s
#define fopen_s( file, name, mode)
Definition: TXLib.h:935
txGetTextExtent
SIZE txGetTextExtent(const char text[], HDC dc=txDC()) tx_nodiscard
Вычисляет размеры текстовой надписи.
TX_BEGIN_MESSAGE_MAP
#define TX_BEGIN_MESSAGE_MAP()
Заголовок карты сообщений (Message Map).
Definition: TXLib.h:6281
_TX_BUFSIZE
const unsigned _TX_BUFSIZE
Размеры внутренних статических строковых буферов TXLib.
Definition: TXLib.h:5547
txGetConsoleCursorPos
POINT txGetConsoleCursorPos()
Возвращает позицию мигающего курсора консоли.
txSetConsoleAttr
unsigned txSetConsoleAttr(unsigned colors=0x07)
Устанавливает цветовые атрибуты консоли.
ROUND
#define ROUND(x)
Округляет число до целого
Definition: TXLib.h:4531
txDialog::this_t
txDialog this_t
Definition: TXLib.h:6053
txDialog::Layout::sx
short sx
Размер по X.
Definition: TXLib.h:6110
txCreateDIBSection
HDC txCreateDIBSection(double sizeX, double sizeY, RGBQUAD **pixels=NULL) tx_nodiscard
Создает аппаратно-независимый дополнительный холст (контекст рисования, Device Context, DC) в памяти с возможностью прямого доступа к нему как к массиву.
txDialog::EDIT
@ EDIT
Редактируемый текст
Definition: TXLib.h:6075
_TX_WAITABLE_PARENTS
#define _TX_WAITABLE_PARENTS
Если определено, не исключать адреса без отладочной информации из трассировок стека.
Definition: TXLib.h:5593
ZERO
#define ZERO(type)
Обнулитель типов, не имеющих конструкторов
Definition: TXLib.h:4707
txSetFillColor
HBRUSH txSetFillColor(COLORREF color, HDC dc=txDC())
Устанавливает текущий цвет заполнения фигур.
TX_GREEN
const COLORREF TX_GREEN
Зеленый цвет.
Definition: TXLib.h:1410
txClearConsole
bool txClearConsole()
Стирает текст консоли.
strncat_s
#define strncat_s(dest, sizeof_dest, src, count)
Definition: TXLib.h:932
getenv_s
#define getenv_s(sz, buf, sizeof_buf, name)
Definition: TXLib.h:951
TX_HANDLE
#define TX_HANDLE(id)
Заголовок обработчика сообщения (Message handler) карты сообщений.
Definition: TXLib.h:6311
_TX_VERSION
#define _TX_VERSION
Definition: TXLib.h:132
TX_BLACK
const COLORREF TX_BLACK
Названия предопределенных цветов.
Definition: TXLib.h:1408
_TX_LOCALE
const char _TX_LOCALE[]
Definition: TXLib.h:5323
txGetColor
COLORREF txGetColor(HDC dc=txDC()) tx_nodiscard
Возвращает текущий цвет линий и текста.
_snprintf_s
#define _snprintf_s
Definition: TXLib.h:941
txCircle
bool txCircle(double x, double y, double r)
Рисует окружность или круг.
txSetWindowsHook
WNDPROC txSetWindowsHook(WNDPROC wndProc=NULL)
Устанавливает альтернативную функцию обработки оконных сообщений Windows (оконную функцию) для окна TXLib.
txCreateCompatibleDC
HDC txCreateCompatibleDC(double sizeX, double sizeY, HBITMAP bitmap=NULL) tx_nodiscard
Создает дополнительный холст (контекст рисования, Device Context, DC) в памяти.
TX_MAGENTA
const COLORREF TX_MAGENTA
Темно-малиновый цвет.
Definition: TXLib.h:1413
txBegin
int txBegin()
Блокирует обновление изображения окна, во избежание мигания.
ctime_s
#define ctime_s(buf, sizeof_buf, time)
Definition: TXLib.h:938
txDialog::setLayout
const Layout * setLayout(const Layout *layout)
Устанавливает текущий макет диалогового окна.
txSleep
double txSleep(double time=0)
Задерживает выполнение программы на определенное время.
txDialog::CONTROL
CONTROL
Константы для задания типа контрола.
Definition: TXLib.h:6072
txOutputDebugPrintf
int txOutputDebugPrintf(const char format[],...) tx_printfy(1)
Выводит всплывающее сообщение в системном трее.
TX_CYAN
const COLORREF TX_CYAN
Бирюзовый цвет.
Definition: TXLib.h:1411
txSetConsoleCursorPos
POINT txSetConsoleCursorPos(double x, double y)
Устанавливает позицию мигающего курсора консоли.
txClear
bool txClear(HDC dc=txDC())
Стирает холст текущим цветом заполнения.
txSetColor
HPEN txSetColor(COLORREF color, double thickness=1, HDC dc=txDC())
Создает (смешивает) цвет из трех базовых цветов (компонент).
_TX_MODULE
#define _TX_MODULE
Имя модуля TXLib. Входит в диагностические сообщения.
Definition: TXLib.h:152
_TX_TIMEOUT
const int _TX_TIMEOUT
Макрос, разрешающий использовать TXLib вместе с графической библиотекой SFML
Definition: TXLib.h:5484
TX_RED
const COLORREF TX_RED
Темно-красный цвет. Слишком темный.
Definition: TXLib.h:1412
_TX_STACKSIZE
const unsigned _TX_STACKSIZE
Минимальный размер стека для потоков программы.
Definition: TXLib.h:5551
txDialog::dialogBox
intptr_t dialogBox(WORD resource)
Запускает диалоговое окно.
txDialog::Layout::style
DWORD style
Стиль контрола
Definition: TXLib.h:6112
TX_ORANGE
const COLORREF TX_ORANGE
Оранжевый цвет.
Definition: TXLib.h:1415
_txWatchdogTimeout
int _txWatchdogTimeout
Лимит времени на завершение программы, начиная от завершения функции main() или от вызова exit(), в мс.
Definition: TXLib.h:5640
txDialog::STATIC
@ STATIC
Нередактируемый элемент (текст, картинка и т.д.)
Definition: TXLib.h:6076
txGetExtent
POINT txGetExtent(HDC dc=txDC()) tx_nodiscard
Возвращает размер окна, картинки или холста в виде структуры POINT.
txDrawText
bool txDrawText(double x0, double y0, double x1, double y1, const char text[], unsigned format=DT_CENTER|DT_VCENTER|DT_WORDBREAK|DT_WORD_ELLIPSIS, HDC dc=txDC())
Рисует текст, размещенный в прямоугольной области.
_TX_ALLOW_KILL_PARENT
#define _TX_ALLOW_KILL_PARENT
Разрешать принудительное завершение вызывающих программ, ждущих нажатия клавиш после завершения TXLib.
Definition: TXLib.h:5625
txDrawMan
void txDrawMan(int x, int y, int sizeX, int sizeY, COLORREF color, double handL, double handR, double twist, double head, double eyes, double wink, double crazy, double smile, double hair, double wind)
Рисует человечка.
Definition: TXLib.h:2112
asserted
#define asserted
Выводит диагностическое сообщение в случае нулевого или ложного результата.
Definition: TXLib.h:4862
txHSL2RGB
COLORREF txHSL2RGB(COLORREF hslColor) tx_nodiscard
Преобразует цвет из формата HSL в формат RGB.
_TX_AUTHOR
#define _TX_AUTHOR
Definition: TXLib.h:133
TX_LIGHTGRAY
const COLORREF TX_LIGHTGRAY
Светло-серый цвет.
Definition: TXLib.h:1418
_TX_BIGBUFSIZE
const unsigned _TX_BIGBUFSIZE
Размеры больших статических буферов.
Definition: TXLib.h:5548
txRedrawWindow
void txRedrawWindow()
Обновляет изображение в окне TXLib вручную.
txGetTextExtentY
int txGetTextExtentY(const char text[], HDC dc=txDC()) tx_nodiscard
Вычисляет высоту текстовой надписи.
txGetTextExtentX
int txGetTextExtentX(const char text[], HDC dc=txDC()) tx_nodiscard
Вычисляет ширину текстовой надписи.
txLock
bool txLock(bool wait=true)
Блокировка холста (контекста рисования).
_TX_VER
#define _TX_VER
Текущая версия библиотеки.
Definition: TXLib.h:131
txDialog
Базовый класс для диалоговых окон.
Definition: TXLib.h:6052
TX_END_MESSAGE_MAP
#define TX_END_MESSAGE_MAP
Завершитель карты сообщений.
Definition: TXLib.h:6366
MIN
#define MIN(a, b)
Возвращает минимальное из двух чисел
Definition: TXLib.h:4507
txUnlock
bool txUnlock()
Разблокировка холста
_tx_auto_func_::_tx_auto_func_
_tx_auto_func_(T func)
Definition: TXLib.h:4755
txRGB2HSL
COLORREF txRGB2HSL(COLORREF rgbColor) tx_nodiscard
Преобразует цвет из формата RGB в формат HSL.
tx_deprecated
#define tx_deprecated
Definition: TXLib.h:989
txUseAlpha
HDC txUseAlpha(HDC image)
Пересчитывает цвета пикселей с учетом прозрачности (переводит цвета в формат Premultiplied Alpha).
strerror_s
#define strerror_s(buf, sizeof_buf, code)
Definition: TXLib.h:933
verify
#define verify
Выполняет команду (вычисляет выражение) и проверяет результат.
Definition: TXLib.h:4907
ID_TEXT_
#define ID_TEXT_
txDialog::DIALOG
@ DIALOG
Начало описания диалога
Definition: TXLib.h:6073
txPie
bool txPie(double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc=txDC())
Рисует сектор эллипса.
txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture
bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()
Делает нечто иногда удобное. См. название функции.
assert
#define assert(cond)
Definition: TXLib.h:4830
In
bool In(Tx x, Ta a, Tb b) tx_nodiscard tx_deprecated
Проверка, находится ли параметр х внутри замкнутого интервала [a; b].
txGDI
#define txGDI(command, dc)
Вызов функции Win32 GDI с автоматической блокировкой и разблокировкой.
Definition: TXLib.h:5291
TX_TRANSPARENT
const COLORREF TX_TRANSPARENT
Прозрачный цвет. Отключает рисование.
Definition: TXLib.h:1427
txSetDefaults
bool txSetDefaults(HDC dc=txDC())
Установка параметров рисования по умолчанию.
_tx_auto_func_::~_tx_auto_func_
~_tx_auto_func_()
Definition: TXLib.h:4756
txMouseButtons
unsigned txMouseButtons() tx_nodiscard
Возвращает состояние Кнопок Мыши!
TX_WHITE
const COLORREF TX_WHITE
Белый цвет.
Definition: TXLib.h:1426
sizearr
#define sizearr(arr)
Добрый дядюшка Принтф. Теперь шаболонный.
Definition: TXLib.h:4308
txVideoMemory
RGBQUAD * txVideoMemory() tx_nodiscard
Возвращает буфер памяти, связанный с холстом (HDC) TXLib.
txDeleteDC
bool txDeleteDC(HDC dc)
Уничтожает холст (контекст рисования, DC) в памяти.
_TX_NOINIT
#define _TX_NOINIT
Запрет ранней инициализации TXLib.
Definition: TXLib.h:5398
txGetFPS
double txGetFPS(int minFrames=txFramesToAverage) tx_nodiscard
Выдает количество кадров (вызовов этой функции) в секунду.
_txLogName
char _txLogName[MAX_PATH]
Имя лог-файла TXLib.
Definition: TXLib.h:5355
txRegQuery
int txRegQuery(const char *keyName, const char *valueName, void *value, size_t szValue)
Читает информацию из реестра Windows.
txDialog::Layout::font
const char * font
Шрифт диалогового окна
Definition: TXLib.h:6114
txFontExist
LOGFONT * txFontExist(const char name[]) tx_nodiscard
Ищет шрифт по его названию.
txAlphaBlend
bool txAlphaBlend(HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource=0, double ySource=0, double alpha=1.0)
Копирует изображение с одного холста (контекста рисования, DC) на другой с учетом полупрозрачности.
txExtractColor
unsigned txExtractColor(COLORREF color, COLORREF component) tx_nodiscard
Извлекает цветовую компоненту (цветовой канал) из смешанного цвета.
TX_SATURATION
const COLORREF TX_SATURATION
Насыщенность цвета в модели HSL.
Definition: TXLib.h:1433
TX_COMMAND_MAP
#define TX_COMMAND_MAP
Начало карты команд (Command map) в карте сообщений.
Definition: TXLib.h:6337
_tx_auto_func
_tx_auto_func_< T > _tx_auto_func(T func)
Definition: TXLib.h:4764
txSetPixel
bool txSetPixel(double x, double y, COLORREF color, HDC dc=txDC())
Рисует пиксель (точку на экране).
txAutoLock::cs_
CRITICAL_SECTION * cs_
Блокируемая критическая секция
Definition: TXLib.h:6005
txAutoLock
Класс для автоматической блокировки и разблокировки критической секции.
Definition: TXLib.h:5920
txDialog::Layout::id
WORD id
Идентификатор контрола
Definition: TXLib.h:6107
_tx_auto_func_
Definition: TXLib.h:4751
txFloodFill
bool txFloodFill(double x, double y, COLORREF color=TX_TRANSPARENT, DWORD mode=FLOODFILLSURFACE, HDC dc=txDC())
Заливает произвольный контур текущим цветом заполнения.
txInputBox
const char * txInputBox(const char *text=NULL, const char *caption=NULL, const char *input=NULL) tx_nodiscard
Ввод строки в отдельном окне.
Definition: TXLib.h:6406
txMousePos
POINT txMousePos() tx_nodiscard
Возвращает позицию Мыши!
txVersionNumber
unsigned txVersionNumber() tx_nodiscard
Возвращает номер версии библиотеки.
TX_LIGHTRED
const COLORREF TX_LIGHTRED
Светло-красный цвет. Не самого лучшего оттенка.
Definition: TXLib.h:1422
txTransparentBlt
bool txTransparentBlt(HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource=0, double ySource=0, COLORREF transColor=TX_BLACK)
Копирует изображение с одного холста (контекста рисования, DC) на другой с учетом прозрачности.
txAutoLock::txAutoLock
txAutoLock(bool mandatory=true)
Конструктор для блокировки холста TXLib.
Definition: TXLib.h:5974
TX_LIGHTNESS
const COLORREF TX_LIGHTNESS
Светлота цвета в модели HSL.
Definition: TXLib.h:1434
TX_GRAY
const COLORREF TX_GRAY
Серый цвет.
Definition: TXLib.h:1416
txVersion
const char * txVersion() tx_nodiscard
Возвращает строку с информацией о текущей версии библиотеки.
txMouseY
int txMouseY() tx_nodiscard
Возвращает Y-Координату Мыши!
TX_ERROR
#define TX_ERROR(...)
Выводит развернутое диагностическое сообщение.
Definition: TXLib.h:4937
txGetConsoleAttr
unsigned txGetConsoleAttr() tx_nodiscard
Возвращает текущие цветовые атрибуты консоли.
txBitBlt
bool txBitBlt(HDC destImage, double xDest, double yDest, double width, double height, HDC sourceImage, double xSource=0, double ySource=0, unsigned operation=SRCCOPY)
Копирует изображение с одного холста (контекста рисования, DC) на другой.
_TX_FATAL_EXCEPTIONS_LIMIT
#define _TX_FATAL_EXCEPTIONS_LIMIT
Максимальное количество фатальных исключений.
Definition: TXLib.h:5563
txPlayVideo
intptr_t txPlayVideo(int x, int y, int width, int height, const char fileName[], double zoom=0, double gain=1, HWND wnd=txWindow())
Проигрывает видео.
txDialog::BUTTON
@ BUTTON
Кнопка
Definition: TXLib.h:6074
txGetConsoleExtent
POINT txGetConsoleExtent()
Возвращает размер консоли.
txChord
bool txChord(double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc=txDC())
Рисует хорду эллипса.
txEnd
int txEnd()
Разблокирует обновление окна, заблокированное функцией txBegin().
random
int random(int range) tx_deprecated
Генератор случайных чисел
txTypename
#define txTypename(value)
Definition: TXLib.h:5068
txDC
HDC & txDC() tx_nodiscard
Возвращает холст (дескриптор контекста рисования, HDC) окна рисования TXLib.
txDialog::Layout
Класс для описания элемента диалогового окна (контрола)
Definition: TXLib.h:6104
txPlaySound
bool txPlaySound(const char filename[]=NULL, DWORD mode=SND_ASYNC)
Воспроизводит звуковой файл.
TX_LIGHTGREEN
const COLORREF TX_LIGHTGREEN
Светло-зеленый цвет.
Definition: TXLib.h:1420
txSelectFont
HFONT txSelectFont(const char name[], double sizeY, double sizeX=-1, int bold=FW_DONTCARE, bool italic=false, bool underline=false, bool strikeout=false, double angle=0, HDC dc=txDC())
Выбирает текущий шрифт, его размер и другие атрибуты.
txArc
bool txArc(double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc=txDC())
Рисует дугу эллипса.
TX_LIGHTBLUE
const COLORREF TX_LIGHTBLUE
Светло-синий цвет.
Definition: TXLib.h:1419
txEllipse
bool txEllipse(double x0, double y0, double x1, double y1, HDC dc=txDC())
Рисует эллипс.
_TX_BUILDMODE
#define _TX_BUILDMODE
Имя режима сборки
Definition: TXLib.h:222
txGetPixel
COLORREF txGetPixel(double x, double y, HDC dc=txDC()) tx_nodiscard
Возвращает текущий цвет точки (пикселя) на экране.
txTextCursor
bool txTextCursor(bool blink=true)
Запрещает или разрешает рисование мигающего курсора в окне.
txDialog::Layout::y
short y
Координата нижнего правого угла
Definition: TXLib.h:6109
txDialog::dialogBox
intptr_t dialogBox(const Layout *layout=NULL, size_t bufsize=0)
Запускает диалоговое окно.
txDialog::~txDialog
virtual ~txDialog()
Деструктор.
Definition: TXLib.h:6149
txDialog::Layout::wndclass
CONTROL wndclass
Тип контрола
Definition: TXLib.h:6105
txDialog::Layout::x
short x
Координата верхнего левого угла
Definition: TXLib.h:6108
txSetLocale
int txSetLocale(int codepage=_TX_CODEPAGE, const char locale[]=_TX_LOCALE, const wchar_t wLocale[]=_TX_WLOCALE)
txDialog::Layout::sy
short sy
Размер по Y.
Definition: TXLib.h:6111
_controlfp_s
#define _controlfp_s(oldCtl, newCtl, mask)
Definition: TXLib.h:939
tx_printfy
#define tx_printfy(formatArgNum)
Definition: TXLib.h:990
txDialog::Layout::fontsize
WORD fontsize
Размер шрифта диалогового окна
Definition: TXLib.h:6115
txTextOut
bool txTextOut(double x, double y, const char text[], HDC dc=txDC())
Рисует текст.
_TX_EXCEPTIONS_LIMIT
#define _TX_EXCEPTIONS_LIMIT
Максимальное количество исключений в программе.
Definition: TXLib.h:5559
_txCursorBlinkInterval
unsigned _txCursorBlinkInterval
Интервал мигания курсора консоли (мс)
Definition: TXLib.h:5453
tx_fpreset
void tx_fpreset()
Переинициализирует математический сопроцессор
txWindow
HWND txWindow() tx_nodiscard
Возвращает дескриптор окна рисования
txRectangle
bool txRectangle(double x0, double y0, double x1, double y1, HDC dc=txDC())
Рисует прямоугольник.
_txSwapBuffers
bool(* _txSwapBuffers)(HDC dest, int xDest, int yDest, int wDest, int hDest, HDC src, int xSrc, int ySrc, int wSrc, int hSrc, DWORD rOp)
Указатель на функцию, выводящую изображение непосредственно в окно TXLib во время обработки WM_PAINT.
Definition: TXLib.h:5539
_vsnprintf_s
#define _vsnprintf_s(str, sz, trunc, format, arg)
Definition: TXLib.h:942
txQueryPerformance
double txQueryPerformance() tx_nodiscard
Оценивает скорость работы компьютера.
txSaveImage
bool txSaveImage(const char filename[], HDC dc=txDC())
Сохраняет в файл изображение в формате BMP.
TX_HUE
const COLORREF TX_HUE
Цветовой тон цвета в модели HSL.
Definition: TXLib.h:1432
txDialog::DialogProc_
static intptr_t CALLBACK DialogProc_(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
Настоящая диалоговая функция (не txDialog::dialogProc(), т.к. функция окна in32 должна быть статической).
_TX_HUGEBUFSIZE
const unsigned _TX_HUGEBUFSIZE
Размеры очень больших статических буферов.
Definition: TXLib.h:5549
txDestroyWindow
bool txDestroyWindow(HWND wnd=txWindow())
Уничтожает окно.
MAX
#define MAX(a, b)
Возвращает максимальное из двух чисел
Definition: TXLib.h:4484
strtok_s
#define strtok_s( buf, delim, ctx)
Definition: TXLib.h:934
txGetModuleFileName
const char * txGetModuleFileName(bool fileNameOnly=true) tx_nodiscard
Возвращает имя исполняемого файла или изначальный заголовок окна TXLib.
_TX_WLOCALE
const wchar_t _TX_WLOCALE[]
Definition: TXLib.h:5328
TX_BLUE
const COLORREF TX_BLUE
Темно-синий цвет. Плохо виден.
Definition: TXLib.h:1409
strncpy_s
#define strncpy_s(dest, sizeof_dest, src, count)
Definition: TXLib.h:930
txMessageBox
int txMessageBox(const char text[]="Муаххаха! :)", const char header[]="TXLib сообщает", unsigned flags=MB_ICONINFORMATION|MB_OKCANCEL)
Выводит сообщение в окне с помощью функции MessageBox.
txPI
const double txPI
Число Пи
Definition: TXLib.h:4569
_strlwr_s
#define _strlwr_s( str, sizeof_str)
Definition: TXLib.h:936
txDemangle
std::string txDemangle(const char *mangledName)
Преобразует декорированное имя С++ в название типа.
wcsncpy_s
#define wcsncpy_s(dest, sizeof_dest, src, count)
Definition: TXLib.h:931
TX_DARKGRAY
const COLORREF TX_DARKGRAY
Темно-серый цвет.
Definition: TXLib.h:1417
txGetConsoleFontSize
POINT txGetConsoleFontSize() tx_nodiscard
Возвращает размеры шрифта консоли.
__TX_COMPILER__
#define __TX_COMPILER__
Имя и версия текущего компилятора
Definition: TXLib.h:189
TX_PINK
const COLORREF TX_PINK
Розовый гламурный :)
Definition: TXLib.h:1424
txDialog::txDialog
txDialog(const Layout *layout)
Конструктор.
TX_LIGHTCYAN
const COLORREF TX_LIGHTCYAN
Светло-бирюзовый цвет.
Definition: TXLib.h:1421
txMouseX
int txMouseX() tx_nodiscard
Возвращает X-Координату Мыши!